From 12585b65f5f1e3aabb3f21f8105c1ee190d759cf Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Thu, 30 Jan 2025 15:48:39 +0200 Subject: [PATCH 01/12] Remove filetypes if they are disabled --- src/admin-settings.cls.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/admin-settings.cls.php b/src/admin-settings.cls.php index 02c4656d0..a0129ee96 100644 --- a/src/admin-settings.cls.php +++ b/src/admin-settings.cls.php @@ -117,6 +117,35 @@ public function save($raw_data) foreach ($data as $k => $v) { if ($child == self::CDN_MAPPING_FILETYPE) { $v = Utility::sanitize_lines($v); + + // Remove from MAPPING FILETYPE IMAGES/CSS/JS if settings are disabled + $remove_type = array(); + if (empty($data2[$k][self::CDN_MAPPING_INC_IMG])) { + $remove_type = array_merge($remove_type, array('jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'avif')); + } + if (empty($data2[$k][self::CDN_MAPPING_INC_CSS])) { + $remove_type[] = 'css'; + } + if (empty($data2[$k][self::CDN_MAPPING_INC_JS])) { + $remove_type[] = 'js'; + } + + if (count($remove_type) > 0) { + $temp = array_filter($v, function ($i_v) use ($remove_type) { + $leave_value = true; + + foreach ($remove_type as $remove) { + if (strpos($i_v, $remove) !== false) { + $leave_value = false; + break; + } + } + + return $leave_value; + }); + + $v = array_values($temp); + } } if ($child == self::CDN_MAPPING_URL) { # If not a valid URL, turn off CDN From b85f3f6e22a16d49b695f026230e4ae7cc98c203 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Thu, 30 Jan 2025 16:11:08 +0200 Subject: [PATCH 02/12] Update default --- data/const.default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/const.default.json b/data/const.default.json index 6a5342f8a..49209986d 100644 --- a/data/const.default.json +++ b/data/const.default.json @@ -182,6 +182,6 @@ "inc_js": ["1"], "inc_css": ["1"], "inc_img": ["1"], - "filetype": [".aac\n.css\n.eot\n.gif\n.jpeg\n.jpg\n.js\n.less\n.mp3\n.mp4\n.ogg\n.otf\n.pdf\n.png\n.svg\n.ttf\n.webp\n.woff\n.woff2"] + "filetype": [".aac\n.eot\n.less\n.mp3\n.mp4\n.ogg\n.otf\n.pdf\n.ttf\n.webp\n.woff\n.woff2"] } } From 5678ab8fbb1039441f7cc5a93e296c417167f154 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Thu, 30 Jan 2025 23:13:41 +0200 Subject: [PATCH 03/12] Remove settings check --- src/admin-settings.cls.php | 39 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/admin-settings.cls.php b/src/admin-settings.cls.php index a0129ee96..3d388cbe3 100644 --- a/src/admin-settings.cls.php +++ b/src/admin-settings.cls.php @@ -118,34 +118,21 @@ public function save($raw_data) if ($child == self::CDN_MAPPING_FILETYPE) { $v = Utility::sanitize_lines($v); - // Remove from MAPPING FILETYPE IMAGES/CSS/JS if settings are disabled - $remove_type = array(); - if (empty($data2[$k][self::CDN_MAPPING_INC_IMG])) { - $remove_type = array_merge($remove_type, array('jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'avif')); - } - if (empty($data2[$k][self::CDN_MAPPING_INC_CSS])) { - $remove_type[] = 'css'; - } - if (empty($data2[$k][self::CDN_MAPPING_INC_JS])) { - $remove_type[] = 'js'; - } - - if (count($remove_type) > 0) { - $temp = array_filter($v, function ($i_v) use ($remove_type) { - $leave_value = true; - - foreach ($remove_type as $remove) { - if (strpos($i_v, $remove) !== false) { - $leave_value = false; - break; - } + // Remove from MAPPING FILETYPE extensions for IMAGES, CSS, JS + $remove_type = array('jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'avif', 'css', 'js'); + $temp = array_filter($v, function ($i_v) use ($remove_type) { + $leave_value = true; + + foreach ($remove_type as $remove) { + if (strpos($i_v, $remove) !== false) { + $leave_value = false; + break; } + } - return $leave_value; - }); - - $v = array_values($temp); - } + return $leave_value; + }); + $v = array_values($temp); } if ($child == self::CDN_MAPPING_URL) { # If not a valid URL, turn off CDN From 9978263d3af2c6b1b82c8b08ede5d09c680a9087 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Fri, 31 Jan 2025 18:26:00 +0200 Subject: [PATCH 04/12] Add css and js rewrite for html content --- src/cdn.cls.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/cdn.cls.php b/src/cdn.cls.php index 87436ce16..f68ec090a 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -209,9 +209,28 @@ private function _finalize() $this->_replace_inline_css(); } + // Temporary add file types to mapping filetype rewrite + // Add CSS + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { + $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; + } + + // Add JS + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { + $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; + } + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_FILETYPE])) { $this->_replace_file_types(); } + + // Remove temporary file types added to mapping filetype rewrite + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { + unset($this->_cfg_cdn_mapping['.css']); + } + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { + unset($this->_cfg_cdn_mapping['.js']); + } } /** From d5fb6a25504e571f327b8300cd635bf21e083a0e Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Fri, 31 Jan 2025 18:53:30 +0200 Subject: [PATCH 05/12] Simplify remove extensions on settings save --- src/admin-settings.cls.php | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/admin-settings.cls.php b/src/admin-settings.cls.php index 3d388cbe3..bf6d45e2a 100644 --- a/src/admin-settings.cls.php +++ b/src/admin-settings.cls.php @@ -119,20 +119,8 @@ public function save($raw_data) $v = Utility::sanitize_lines($v); // Remove from MAPPING FILETYPE extensions for IMAGES, CSS, JS - $remove_type = array('jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'avif', 'css', 'js'); - $temp = array_filter($v, function ($i_v) use ($remove_type) { - $leave_value = true; - - foreach ($remove_type as $remove) { - if (strpos($i_v, $remove) !== false) { - $leave_value = false; - break; - } - } - - return $leave_value; - }); - $v = array_values($temp); + $remove_type = array('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif', '.css', '.js'); + $v = array_diff($v, $remove_type); } if ($child == self::CDN_MAPPING_URL) { # If not a valid URL, turn off CDN From f46592c636b58a006acfbdf0593876889ebb1868 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Fri, 31 Jan 2025 19:02:12 +0200 Subject: [PATCH 06/12] CDN - Move file type include to init --- src/cdn.cls.php | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/cdn.cls.php b/src/cdn.cls.php index f68ec090a..05a9157c8 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -94,6 +94,16 @@ public function init() Debug2::debug2('[CDN] mapping ' . implode(',', $v[Base::CDN_MAPPING_FILETYPE]) . ' -> ' . $this_url); } } + + // Add CSS to rewrite if CDN Mapping setting is enabled + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { + $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; + } + + // Add JS to rewrite if CDN Mapping setting is enabled + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { + $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; + } if (!$this->_cfg_url_ori || !$this->_cfg_cdn_mapping) { if (!defined(self::BYPASS)) { @@ -209,28 +219,9 @@ private function _finalize() $this->_replace_inline_css(); } - // Temporary add file types to mapping filetype rewrite - // Add CSS - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { - $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; - } - - // Add JS - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { - $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; - } - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_FILETYPE])) { $this->_replace_file_types(); } - - // Remove temporary file types added to mapping filetype rewrite - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { - unset($this->_cfg_cdn_mapping['.css']); - } - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { - unset($this->_cfg_cdn_mapping['.js']); - } } /** From 5aee6386e3b9abbd1a4b7a06c41d96824bb0acbc Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Fri, 31 Jan 2025 19:09:31 +0200 Subject: [PATCH 07/12] Add filter + formatting --- src/admin-settings.cls.php | 12 +++++++++++- src/cdn.cls.php | 6 +++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/admin-settings.cls.php b/src/admin-settings.cls.php index bf6d45e2a..19c158874 100644 --- a/src/admin-settings.cls.php +++ b/src/admin-settings.cls.php @@ -119,7 +119,17 @@ public function save($raw_data) $v = Utility::sanitize_lines($v); // Remove from MAPPING FILETYPE extensions for IMAGES, CSS, JS - $remove_type = array('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif', '.css', '.js'); + $remove_type = apply_filters('litespeed_cdn_save_filetypes_remove', array( + '.jpg', + '.jpeg', + '.png', + '.gif', + '.svg', + '.webp', + '.avif', + '.css', + '.js', + )); $v = array_diff($v, $remove_type); } if ($child == self::CDN_MAPPING_URL) { diff --git a/src/cdn.cls.php b/src/cdn.cls.php index 05a9157c8..b868d5761 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -94,12 +94,12 @@ public function init() Debug2::debug2('[CDN] mapping ' . implode(',', $v[Base::CDN_MAPPING_FILETYPE]) . ' -> ' . $this_url); } } - + // Add CSS to rewrite if CDN Mapping setting is enabled if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { - $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; + $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; } - + // Add JS to rewrite if CDN Mapping setting is enabled if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; From a692a9e27b61a343be9a0a108a7f76c47c4bd563 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Fri, 31 Jan 2025 20:11:15 +0200 Subject: [PATCH 08/12] Process images links from element attribute --- src/cdn.cls.php | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/cdn.cls.php b/src/cdn.cls.php index b868d5761..20502567e 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -95,6 +95,14 @@ public function init() } } + // Add IMAGES to rewrite if CDN Mapping setting is enabled + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG])) { + $add_images_type = apply_filters('litespeed_cdn_add_filetypes_image', array('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif')); + foreach ($add_images_type as $ext) { + $this->_cfg_cdn_mapping[$ext] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG]; + } + } + // Add CSS to rewrite if CDN Mapping setting is enabled if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; @@ -164,25 +172,6 @@ private function _append_cdn_mapping($filetype, $url) } } - /** - * If include css/js in CDN - * - * @since 1.6.2.1 - * @return bool true if included in CDN - */ - public function inc_type($type) - { - if ($type == 'css' && !empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { - return true; - } - - if ($type == 'js' && !empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { - return true; - } - - return false; - } - /** * Run CDN process * NOTE: As this is after cache finalized, can NOT set any cache control anymore @@ -196,6 +185,8 @@ public function finalize($content) $this->content = $content; $this->_finalize(); + + Debug2::debug2('[TIMMM] mapping ' . print_r($this->content, true)); return $this->content; } From 4d3f966f405fa39c1960a62bb2bdc2824333cc90 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Wed, 5 Feb 2025 02:08:34 +0200 Subject: [PATCH 09/12] Remove debug log --- src/cdn.cls.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdn.cls.php b/src/cdn.cls.php index 20502567e..308f09c20 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -186,7 +186,6 @@ public function finalize($content) $this->_finalize(); - Debug2::debug2('[TIMMM] mapping ' . print_r($this->content, true)); return $this->content; } From d7f9253a3e1fd39bcaa179e88b18dd910b4354f4 Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Tue, 24 Jun 2025 19:00:07 +0300 Subject: [PATCH 10/12] Fixes --- src/admin-settings.cls.php | 110 +++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/src/admin-settings.cls.php b/src/admin-settings.cls.php index 6befc2a55..904a6f63f 100644 --- a/src/admin-settings.cls.php +++ b/src/admin-settings.cls.php @@ -84,8 +84,8 @@ public function save( $raw_data ) { } switch ($id) { case self::O_CRAWLER_ROLES: // Don't allow Editor/admin to be used in crawler role simulator - $data = Utility::sanitize_lines($data); - if ($data) { + $data = Utility::sanitize_lines($data); + if ($data) { foreach ($data as $k => $v) { if (user_can($v, 'edit_posts')) { $msg = sprintf( @@ -99,57 +99,59 @@ public function save( $raw_data ) { } break; case self::O_CDN_MAPPING: - /** - * CDN setting - * - * Raw data format: - * cdn-mapping[url][] = 'xxx' - * cdn-mapping[url][2] = 'xxx2' - * cdn-mapping[inc_js][] = 1 - * - * Final format: - * cdn-mapping[ 0 ][ url ] = 'xxx' - * cdn-mapping[ 2 ][ url ] = 'xxx2' - */ - if ($data) { - foreach ($data as $k => $v) { - if ($child == self::CDN_MAPPING_FILETYPE) { - $v = Utility::sanitize_lines($v); - // Remove from MAPPING FILETYPE extensions for IMAGES, CSS, JS - $remove_type = apply_filters('litespeed_cdn_save_filetypes_remove', array( - '.jpg', - '.jpeg', - '.png', - '.gif', - '.svg', - '.webp', - '.avif', - '.css', - '.js', - )); - $v = array_diff($v, $remove_type); - } - if ($child == self::CDN_MAPPING_URL) { - // If not a valid URL, turn off CDN - if (strpos($v, 'https://') !== 0) { - self::debug('❌ CDN mapping set to OFF due to invalid URL'); - $the_matrix[self::O_CDN] = false; - } - $v = trailingslashit($v); - } - if (in_array($child, array( self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS ))) { - // Because these can't be auto detected in `config->update()`, need to format here - $v = $v === 'false' ? 0 : (bool) $v; - } - - if (empty($data2[$k])) { - $data2[$k] = array(); - } - $data2[$k][$child] = $v; - } - } - - $data = $data2; + /** + * CDN setting + * + * Raw data format: + * cdn-mapping[url][] = 'xxx' + * cdn-mapping[url][2] = 'xxx2' + * cdn-mapping[inc_js][] = 1 + * + * Final format: + * cdn-mapping[ 0 ][ url ] = 'xxx' + * cdn-mapping[ 2 ][ url ] = 'xxx2' + */ + if ($data && is_array($data)) { + foreach ($data as $k => $v) { + if ($child == self::CDN_MAPPING_FILETYPE) { + $v = Utility::sanitize_lines($v); + + // Remove from MAPPING FILETYPE extensions for IMAGES, CSS, JS + $remove_type = apply_filters('litespeed_cdn_save_filetypes_remove', array( + '.jpg', + '.jpeg', + '.png', + '.gif', + '.svg', + '.webp', + '.avif', + '.css', + '.js', + )); + $v = array_values(array_diff($v, $remove_type)); + } + if ($child == self::CDN_MAPPING_URL) { + # If not a valid URL, turn off CDN + if (strpos($v, 'https://') !== 0) { + self::debug('❌ CDN mapping set to OFF due to invalid URL'); + $the_matrix[self::O_CDN] = false; + } + $v = trailingslashit($v); + } + if (in_array($child, array(self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS))) { + // Because these can't be auto detected in `config->update()`, need to format here + $v = $v === 'false' ? 0 : (bool) $v; + } + + if (empty($data2[$k])) { + $data2[$k] = array(); + } + + $data2[$k][$child] = $v; + } + } + + $data = $data2; break; case self::O_CRAWLER_COOKIES: @@ -221,7 +223,7 @@ public function save( $raw_data ) { $the_matrix[$id] = $data; } - + // Special handler for CDN/Crawler 2d list to drop empty rows foreach ($the_matrix as $id => $data) { /** From b81e05a1c8be42a72e0fca1c2c48233bc71ab57e Mon Sep 17 00:00:00 2001 From: Tim LSC Date: Wed, 17 Sep 2025 10:19:00 +0300 Subject: [PATCH 11/12] Merge branch 'dev' into CDN-filetype_remove --- .github/workflows/ci.yml | 10 +- .prettierignore | 2 - assets/css/fonts/litespeedfont.eot | Bin 2028 -> 0 bytes assets/css/fonts/litespeedfont.svg | 11 - assets/css/fonts/litespeedfont.ttf | Bin 1840 -> 0 bytes assets/css/fonts/litespeedfont.woff | Bin 1916 -> 0 bytes assets/css/litespeed-dummy.css | 1 + assets/css/litespeed.css | 71 +- assets/js/lazyload.init.js | 13 +- assets/js/lazyload.lib.js | 6 +- assets/js/lazyload.min.js | 2 +- autoload.php | 1 + cli/crawler.cls.php | 83 +- cli/database.cls.php | 227 ++ cli/debug.cls.php | 26 +- cli/image.cls.php | 92 +- cli/online.cls.php | 205 +- cli/option.cls.php | 288 +- cli/presets.cls.php | 51 +- cli/purge.cls.php | 225 +- composer.json | 6 +- composer.lock | 97 +- lang/litespeed-cache.pot | 3608 +++++++++-------- lib/css_js_min/minify/css.cls.php | 5 +- lib/css_js_min/minify/exception.cls.php | 1 + lib/css_js_min/minify/js.cls.php | 1 + lib/css_js_min/minify/minify.cls.php | 1 + .../pathconverter/converter.cls.php | 1 + lib/guest.cls.php | 1 + lib/html-min.cls.php | 1 + lib/object-cache.php | 1 + lib/php-compatibility.func.php | 4 +- lib/urirewriter.cls.php | 1 + litespeed-cache.php | 169 +- package.json | 9 +- phpcs.ruleset.xml | 25 +- readme.txt | 81 +- src/activation.cls.php | 420 +- src/admin-display.cls.php | 1598 +++++--- src/admin-settings.cls.php | 484 +-- src/admin.cls.php | 178 +- src/api.cls.php | 257 +- src/avatar.cls.php | 326 +- src/base.cls.php | 453 ++- src/cdn.cls.php | 497 ++- src/cdn/cloudflare.cls.php | 94 +- src/cdn/quic.cls.php | 48 +- src/cloud.cls.php | 1669 ++++---- src/conf.cls.php | 454 ++- src/control.cls.php | 684 ++-- src/core.cls.php | 628 +-- src/crawler-map.cls.php | 5 +- src/crawler.cls.php | 58 +- src/css.cls.php | 1 + src/data.cls.php | 64 +- src/data.upgrade.func.php | 645 +-- src/db-optm.cls.php | 24 +- src/debug2.cls.php | 48 +- src/doc.cls.php | 17 +- src/error.cls.php | 214 +- src/esi.cls.php | 25 +- src/file.cls.php | 9 +- src/gui.cls.php | 10 +- src/health.cls.php | 3 +- src/htaccess.cls.php | 13 +- src/img-optm.cls.php | 134 +- src/import.cls.php | 5 +- src/import.preset.cls.php | 1 + src/lang.cls.php | 9 +- src/localization.cls.php | 1 + src/media.cls.php | 1156 +++--- src/metabox.cls.php | 5 +- src/object-cache-wp.cls.php | 981 +++++ src/object-cache.cls.php | 685 +++- src/object.lib.php | 919 +---- src/optimize.cls.php | 210 +- src/optimizer.cls.php | 3 +- src/placeholder.cls.php | 5 +- src/purge.cls.php | 39 +- src/report.cls.php | 3 +- src/rest.cls.php | 1 + src/root.cls.php | 57 +- src/router.cls.php | 29 +- src/str.cls.php | 93 +- src/tag.cls.php | 2 +- src/task.cls.php | 4 +- src/tool.cls.php | 110 +- src/ucss.cls.php | 1 + src/utility.cls.php | 46 +- src/vary.cls.php | 9 +- src/vpi.cls.php | 5 + thirdparty/aelia-currencyswitcher.cls.php | 4 +- thirdparty/amp.cls.php | 4 +- thirdparty/autoptimize.cls.php | 1 + thirdparty/avada.cls.php | 1 + thirdparty/bbpress.cls.php | 1 + thirdparty/beaver-builder.cls.php | 1 + thirdparty/caldera-forms.cls.php | 1 + thirdparty/divi-theme-builder.cls.php | 1 + thirdparty/elementor.cls.php | 1 + thirdparty/entry.inc.php | 1 + thirdparty/facetwp.cls.php | 1 + thirdparty/gravity-forms.cls.php | 1 + thirdparty/litespeed-check.cls.php | 1 + thirdparty/nextgengallery.cls.php | 1 + thirdparty/perfmatters.cls.php | 1 + thirdparty/theme-my-login.cls.php | 1 + thirdparty/user-switching.cls.php | 1 + thirdparty/wc-pdf-product-vouchers.cls.php | 1 + thirdparty/wcml.cls.php | 1 + thirdparty/woo-paypal.cls.php | 1 + thirdparty/woocommerce.cls.php | 10 +- thirdparty/woocommerce.content.tpl.php | 1 + thirdparty/woocommerce.tab.tpl.php | 12 +- thirdparty/wp-polls.cls.php | 1 + thirdparty/wp-postratings.cls.php | 1 + thirdparty/wpdiscuz.cls.php | 4 +- thirdparty/wplister.cls.php | 1 + thirdparty/wpml.cls.php | 1 + thirdparty/wptouch.cls.php | 1 + thirdparty/yith-wishlist.cls.php | 1 + tpl/banner/cloud_news.tpl.php | 2 +- tpl/cache/entry.tpl.php | 50 + tpl/cache/entry_network.tpl.php | 59 - tpl/cache/settings-cache.tpl.php | 4 +- tpl/cache/settings-esi.tpl.php | 24 +- tpl/cache/settings_inc.browser.tpl.php | 4 +- tpl/cdn/qc.tpl.php | 3 +- tpl/crawler/blacklist.tpl.php | 4 +- tpl/crawler/entry.tpl.php | 4 +- tpl/crawler/summary.tpl.php | 20 +- tpl/dash/entry.tpl.php | 2 +- tpl/db_optm/entry.tpl.php | 2 +- tpl/db_optm/manage.tpl.php | 8 +- tpl/general/entry.tpl.php | 2 +- tpl/general/network_settings.tpl.php | 2 +- tpl/general/online.tpl.php | 2 +- tpl/general/settings.tpl.php | 4 +- tpl/img_optm/entry.tpl.php | 2 +- tpl/img_optm/network_settings.tpl.php | 2 +- tpl/img_optm/settings.tpl.php | 28 +- tpl/img_optm/summary.tpl.php | 7 +- tpl/inc/disabled_all.php | 18 - tpl/inc/modal.deactivation.php | 140 + tpl/inc/show_display_installed.php | 3 +- tpl/inc/show_error_cookie.php | 6 +- tpl/optimax/entry.tpl.php | 53 + tpl/optimax/settings.tpl.php | 43 + tpl/optimax/summary.tpl.php | 38 + tpl/page_optm/entry.tpl.php | 2 +- tpl/page_optm/settings_css.tpl.php | 2 + tpl/page_optm/settings_media.tpl.php | 40 +- tpl/presets/standard.tpl.php | 10 +- tpl/toolbox/beta_test.tpl.php | 12 + tpl/toolbox/purge.tpl.php | 32 + tpl/toolbox/report.tpl.php | 10 +- tpl/toolbox/settings-debug.tpl.php | 24 + typos.toml | 7 + 158 files changed, 11147 insertions(+), 8352 deletions(-) delete mode 100644 assets/css/fonts/litespeedfont.eot delete mode 100644 assets/css/fonts/litespeedfont.svg delete mode 100644 assets/css/fonts/litespeedfont.ttf delete mode 100644 assets/css/fonts/litespeedfont.woff create mode 100644 assets/css/litespeed-dummy.css create mode 100644 cli/database.cls.php create mode 100644 src/object-cache-wp.cls.php delete mode 100644 tpl/cache/entry_network.tpl.php delete mode 100644 tpl/inc/disabled_all.php create mode 100644 tpl/inc/modal.deactivation.php create mode 100644 tpl/optimax/entry.tpl.php create mode 100644 tpl/optimax/settings.tpl.php create mode 100644 tpl/optimax/summary.tpl.php create mode 100644 typos.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bacbe87c4..dac948141 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.2' # Adjust to your project's PHP version + php-version: '8.2' coverage: none - name: Initialize Node.js @@ -49,3 +49,11 @@ jobs: - name: Run format check run: npm run format-check + + - name: Install typos + run: | + curl -LsSf https://github.com/crate-ci/typos/releases/download/v1.35.1/typos-v1.35.1-x86_64-unknown-linux-musl.tar.gz | tar xz + sudo mv typos /usr/local/bin/ + + - name: Run typos check + run: typos diff --git a/.prettierignore b/.prettierignore index 32f938100..88391ce21 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,10 +12,8 @@ assets/js/webfontloader.js lib tpl/banner tpl/esi_widget_edit.php -tpl/inc/api_key.php tpl/inc/check_cache_disabled.php tpl/inc/check_if_network_disable_all.php -tpl/inc/modal.header.php !tpl/cache/network_settings-browser.tpl.php !tpl/cache/network_settings-object.tpl.php !tpl/cache/settings-browser.tpl.php diff --git a/assets/css/fonts/litespeedfont.eot b/assets/css/fonts/litespeedfont.eot deleted file mode 100644 index 5985224e670c3477829ba1637daa5dd40ab28f25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2028 zcma)7U1%It6h3Eu)5IjAv%64&42kTDmG$14*~uoPe}bMLu3 zd+*(~i3DCIjgA`AHCD_s&mHBJHnl5Pw>R&O5uKzJTBa^Vv_@^T5jALmTGRwbG(s1^ zEYc;YYSaOfr#UbksFxF!OOnbY5V`kA5U4_i=ylRb%s&6PH~N72D3~wtzFc3ewF`5% z-hyZh_~K%1t&RSEVAT&Su52s}eSQ5m;B_MXv8AZi82*;tB{DuiJX``{f2Ds1{1foQ zORL@WK_WAGh@6#Hy+)7lr@#*4_pR2}+cd(j0lUC%v$h&-{!;!Pcm{L)&~B}DQ76ns z4djxp6JJx~X&-1iVBSf4{+)b2p>%!5D&>gH5ZxyIM(+mA=y(x3_$W%|swSCD%{Ycw z+GEND8&pL=vwpeRL9s&Zw#IK^CLS>}mx%dRqzuqGTKEk_C~CmcD( zIXrNjycABM$~iBuRQa6nq{#cigX4R7-wXX|mVqa|P$CqBN)`HnkYON2SW(l4m10n_ zWezh*HLbAZgcz6EQamhQk;wuC$VoXFii%okGFg@V(+CEYl8s+N z=AA;`8s!oEc*RmNe3Fyq45L~EY_pJK|3Q0#+2_h+rBq@4FNVdAX6d@6#g0OW?P>?! zVXyIS3ZCWo#nemsEoe2~;_Kk{w8Zw(aMm4yk7iE+XvQ0wp&J&m^x0FUZUP#50^_&$ z$32;-eN#%K8^9RTyI!BK6B``iB78M^+MffcHHw=xoaTEDJ{J=QcdeRg;8 zZ#JGlrS_0s*HE@Tpf}D~Bj$VtKK^qWKc~OXLHu646UNmTWhZ+Ddt-C^>Xny9Pyazf zMzYO*y!i8wa?(EA5jdByA5%k4vcDv#HgU8*qCND5RMFwPU$AUX69zn^57d>NxE zI~X|uSu;Z0*|5Mnvf0p?sPTo@HTi{!nKZs-dmO3|#6JSmv zlKL8s{tq)>2meCm8}L8Rd=r;}m-!a>%b7n&0qw3VccZm-6g3uF&F=r-F>_I8ZMoHS zWm)|9#d6GXjw!nkEnZrwb&lR@$ILy8g}94RGwRg3QNx|zaL?6S=Uc62na*Yxri-gG l!qurR$Xar#Hpr!U+CY1b>QJ4>t1j0vzFu;_^nc!?{y!%SFHQge diff --git a/assets/css/fonts/litespeedfont.svg b/assets/css/fonts/litespeedfont.svg deleted file mode 100644 index 10e5f9150..000000000 --- a/assets/css/fonts/litespeedfont.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - \ No newline at end of file diff --git a/assets/css/fonts/litespeedfont.ttf b/assets/css/fonts/litespeedfont.ttf deleted file mode 100644 index 63ded33f765288312d0c70a4493a33a111af414b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1840 zcma)7U1%It6h3F}%xs#NM09o+N{}UyU9qy>J2N}kgj(|;#Xi(Pt?^Me*~t$iyUS*a z2^LWhtSBfVf)9eAFHtCc6cjC3eKGh@@J-qWK`7=iJ{aq)-@P+wwmwMC&fIgp@BG|z zX1KG&L^MLLlOQ?&{FDCpL(b!1zQO(S(nh0GT)6ob5l;YLTxo1}pdSR*dT3>RdwJx$ zYrg|;5s4>O<3@Az2l{}>{v7^j6-4e^@gwlBz>ltO^tOhH9CHzQ>+PimJ;q-Ed+08}XIf)t`Xpu*c7x_GS-#LUQPVN+bmF`?{X>v9$~4y;Sob6z~Zh zHz2H2?vpv9J0!06uhX2s4WE7%hf*?z-c8>V@TD4C8f;S?dYFdD!jGwRhx-5Y`{3xF z&HdksHu47RGBKjY2RMspdd7qt#r;1wL{9&PxGTQKd%MBU@EW(c%dhaO7(s?AEGrpS z>Rg*F&oI|nRe5UC&D9x5x>KH1uBW7%m!6v7JT4esK}oMz=e%FgQH8wpl`I6(hY|RN zz>k7iR-v!_NWm3GIw}f6siIKHsHV4#YUQw&Q+e#9^tRGfQ*u&eS6O9rMF-1phHGkC zQz+vXOhQq2f~%rabd1goWkq3_dqb$4EAu+QguPM+yEWqjqyqAC_-fN{_(a($Uc z_n0Kqq!~bhoL1A3tm#S3W_1ou!x`2pIXnqf@QMX@oX7C+%9V2TG@IoNqgyy~PBG8H z!?`KufNRsWN{z+e3`so86~eU=Pa`9H)}eKzE#6DPbDX@GT1M|6*5Vz$25#RnvY)y$ zbp$@0KLudfZ&pgSO(<3&A-`v3Z3Rg#FB-!#gJ&8amSp+41D(2EFNcY zz+v)XvKuA!1Z|tMg0peu>N{`0G=BO|8nMkW`{m-T5pAS8I}!LU;XI~-9CN-YjTd(Z zPF)(|q@EP$G+=yBAj9O*Bm9xcaQHH+YB{WIK(>t0)vPYlD7f?#Ta=?TTYo`D>Di38 zVSky~4!*pV%yz-IGkcg$(oOx{uup(F2~TQUG)A9fwgCS^X4|m8%4~-U6wZ5AnfRXZ`M-vDx}ia%?|kQ+ zd(N3~@8!AKSz?0ZwrIrcGPO0nU*=wT(kJ2}uotzCJ?uQbxYk$)w*vcu*5dr_x0V|l zn)?d2j@5c>xwW|j?iP`FLhHykH-1}*8%-kn5BMXkN57-@S3nXuCtyFOHTSjner2t9 z1>AG6J*~qqTAf85cM$G5(jE?ea?Z#RR?iyE@-U#yO84fLBn`E7m#h7j#r1FMTBof%E|azYzFQFvBYJl^-d%!bnF&K`2!eDjC&u z*{D_yYdMuiC8f(sS53+ZmHo;pqboXChSOYAQ<}mte!(OZbtm{$bc&AAxuL8m40CS? zm2+iY2bfTRaa8h5zbWG@PZm|NAOnnpUXkm|Ji5mup(f1$66BPcieybsYKql4I0a`| ztK{${RKY72+%X=-$17LL(bH^}(~NH6$T`J42an_?nFFp()haa>e={WUELRBEN<3{D z*|q*d2fM|)sqs7~FQv%r+laMzn{R;IvyAMe?rb|SKAAfS!?NG7Y+<{|5_2aV;lQ+o zF_UZi$?? z1M>go-)ugGPIDt-(L&o|NHkBoqs~HReEQc6K4-AcVe&z;6D9QoZJWD-yK(jUJ8!=- zcIrB7(E^-K5cM(DQ-x5o%_%>Bk?)gtZ=+&aUMlX_A^r@_X10vX1;^(cR6 zG92DAFCrcq7^Jiux8rW37dJ}_o29dhopYT|yGpa^FjT^!F~-t!G|a$Fa0|4F^(-wS>Ks%L NUd4Y;zw!@|e*u#M9uWWl diff --git a/assets/css/litespeed-dummy.css b/assets/css/litespeed-dummy.css new file mode 100644 index 000000000..bc908df5f --- /dev/null +++ b/assets/css/litespeed-dummy.css @@ -0,0 +1 @@ +/* To be replaced in `head` to control optm data location */ \ No newline at end of file diff --git a/assets/css/litespeed.css b/assets/css/litespeed.css index 3e52458c7..e6a7c74eb 100644 --- a/assets/css/litespeed.css +++ b/assets/css/litespeed.css @@ -1,11 +1,6 @@ @font-face { - font-family: 'litespeedfont'; - src: url('fonts/litespeedfont.eot?rs8ttq'); - src: - url('fonts/litespeedfont.eot?rs8ttq#iefix') format('embedded-opentype'), - url('fonts/litespeedfont.ttf?rs8ttq') format('truetype'), - url('fonts/litespeedfont.woff?rs8ttq') format('woff'), - url('fonts/litespeedfont.svg?rs8ttq#litespeedfont') format('svg'); + font-family: "litespeedfont"; + src: url(data:application/font-woff;base64,d09GRgABAAAAAAd8AAsAAAAABzAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgDxIFKmNtYXAAAAFoAAAAVAAAAFQXVtKHZ2FzcAAAAbwAAAAIAAAACAAAABBnbHlmAAABxAAAAywAAAMsC7+w5mhlYWQAAATwAAAANgAAADYNxQCSaGhlYQAABSgAAAAkAAAAJAe+A8ZobXR4AAAFTAAAABQAAAAUCgAABWxvY2EAAAVgAAAADAAAAAwAKAGqbWF4cAAABWwAAAAgAAAAIAAOAX5uYW1lAAAFjAAAAc4AAAHOiN8uy3Bvc3QAAAdcAAAAIAAAACAAAwAAAAMDAAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA6QADwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADgAAAAKAAgAAgACAAEAIOkA//3//wAAAAAAIOkA//3//wAB/+MXBAADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAACAAF/8QD/AO7AIAAxAFEAWkBbgFyAXcBewAAATA0MTQmMTA0JzgBNSImOQEBOAExLgEjIgYHOAE5AQEwBiMUMDEGFDEwBhUwFDEcARUcARUwFDEUFjEwFBc4ARUyFjkBATAWFTAyMTAWMzAyFTAyMzIWMzI2MzoBMTQyMTI2MTAyMTQ2OQEBMDYzNDAxNjQxMDY1MDQxNDY1JjQ1BzEVBzgBMQE4ATEwBjEjMCIxMCIxMCIxMCYxOAExATgBMSc1MDQxMDQ5ATUxNzgBMQEwNjMyFjEBOAExFxUwFDEwFDEnMCIxMDQxMDQxMCIxNDAnMSc4ATEuASMiBgc4ATEHBjAVMCIxMBQxMBQxMCIxHAExMBQVMDIxMBQxMBQxMDIxFDAXMRcWMDM4ARUwMjE4ATEyMBU6ATEwMjM0MDM4ATEwMjE0MDEyMDcxNzYwNTAyMTA0MTA0MTgBMzwBMTA0NScHFzgBMRYUFxYGDwEOASMiJicmNj8BJyY2PwE+ATMyFhcWBgcFFxUBMxMHIwEBMwE1NzUnNQED+wEBAQH+FAIGAwMGAv4UAQEBAQEBAQEB7AIBAQEBAQEBAQEBAQEBAQEBAQECAewBAQEBAQFOAf5XAQEBAQEB/lcBAQGpAgEBAgGpAbABAQH0AgICAgIC9AEBAQEBAfQBAQEBAQEBAQEBAQH0AQEBoE8rAQEBAgSBAgQDBAYBAgEDTysFAwWBAgQEAwYBAgED/oz6/sw6+vo6ATQBNDb+zP7+ATgBwwEBAQEBAQIB7AICAgL+FAIBAQEBAQEBAQEBAQEBAQEBAQEC/hQBAQEBAQEBAQEBAewCAQEBAQEBAQEBAQEBBAEB/lcBAQGpAQEBAQEBAakBAf5XAQEBAQMBAQEB9AECAgH0AQEBAQEBAQEBAQEB9AEBAQEBAfQBAQEBAQEBAYRkPQECAQcLA2MCAgQDAwgDZD4GDgViAgIEAwMIA6P5OgEzATP6ATT+lP7MNv44/jb+zAABAAAAAQAAiK6LiV8PPPUACwQAAAAAANVU3gsAAAAA1VTeCwAA/8QD/AO7AAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAP8AAEAAAAAAAAAAAAAAAAAAAAFBAAAAAAAAAAAAAAAAgAAAAQAAAUAAAAAAAoAFAAeAZYAAQAAAAUBfAAIAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAA0AAAABAAAAAAACAAcAlgABAAAAAAADAA0ASAABAAAAAAAEAA0AqwABAAAAAAAFAAsAJwABAAAAAAAGAA0AbwABAAAAAAAKABoA0gADAAEECQABABoADQADAAEECQACAA4AnQADAAEECQADABoAVQADAAEECQAEABoAuAADAAEECQAFABYAMgADAAEECQAGABoAfAADAAEECQAKADQA7GxpdGVzcGVlZGZvbnQAbABpAHQAZQBzAHAAZQBlAGQAZgBvAG4AdFZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGxpdGVzcGVlZGZvbnQAbABpAHQAZQBzAHAAZQBlAGQAZgBvAG4AdGxpdGVzcGVlZGZvbnQAbABpAHQAZQBzAHAAZQBlAGQAZgBvAG4AdFJlZ3VsYXIAUgBlAGcAdQBsAGEAcmxpdGVzcGVlZGZvbnQAbABpAHQAZQBzAHAAZQBlAGQAZgBvAG4AdEZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=); font-weight: normal; font-style: normal; } @@ -57,9 +52,11 @@ input[type='checkbox'].litespeed-tiny-toggle { background: none; box-shadow: none; } + input[type='checkbox'].litespeed-tiny-toggle:focus { box-shadow: none; } + input[type='checkbox'].litespeed-tiny-toggle:after { content: ''; font-size: 8px; @@ -74,6 +71,7 @@ input[type='checkbox'].litespeed-tiny-toggle:after { border-radius: 72px; box-shadow: 0 0 12px rgb(0 0 0 / 15%) inset; } + input[type='checkbox'].litespeed-tiny-toggle:before { content: ''; width: 14px; @@ -100,6 +98,7 @@ input[type='checkbox'].litespeed-tiny-toggle:checked:before, input[type='checkbox'].litespeed-tiny-toggle:checked:after { transition: ease 0.15s; } + input[type='checkbox'].litespeed-tiny-toggle:checked:after { /*content: 'ON';*/ background-color: #2271b1; @@ -108,10 +107,12 @@ input[type='checkbox'].litespeed-tiny-toggle:checked:after { .block-editor__container input[type='checkbox'].litespeed-tiny-toggle { border: 0 !important; } + .block-editor__container input[type='checkbox'].litespeed-tiny-toggle:before { top: 5px; left: 7px; } + .block-editor__container input[type='checkbox'].litespeed-tiny-toggle:checked:before { left: 23px; } @@ -549,6 +550,11 @@ button.litespeed-form-action:hover { max-width: 960px; } +.litespeed-desc-wrapper{ + display: inline-block; + margin-left: 10px; +} + /* inside stripped table */ .litespeed-desc { font-size: 12px; @@ -3192,6 +3198,11 @@ a.litespeed-redetect { color: #a5caf2; } +.litespeed-overwrite{ + display: inline-block; + margin-left: 10px; +} + @media screen and (min-width: 1401px) { .litespeed-postbox--quiccloud.litespeed-postbox .inside .litespeed-title { padding-left: 20px; @@ -3663,6 +3674,7 @@ p.litespeed-dashboard-unlock-footer { .litespeed-body tbody > tr > th.litespeed-padding-left { padding-left: 3em; } + @media screen and (max-width: 680px) { .litespeed-body tbody > tr > th.litespeed-padding-left { padding-left: 10px; @@ -4298,19 +4310,6 @@ a.litespeed-media-href svg:hover { word-break: break-word; } - input#input_api_key + .button { - margin-top: 10px; - margin-left: 0; - } - - input#input_api_key + .button + .litespeed-desc { - display: block; - } - - input#input_api_key + .button + .litespeed-desc + .button { - margin-left: 0; - } - .litespeed-body .litespeed-table td .litespeed-right { float: none !important; } @@ -4417,3 +4416,35 @@ a.litespeed-media-href svg:hover { color: #dbdbdb; font-size: 1rem; } + +/* ======================================= + Deactivate modal +======================================= */ +#litespeed-modal-deactivate { + padding: 20px; +} + +#litespeed-modal-deactivate h2 { + margin: 0px; +} + +#litespeed-modal-deactivate .litespeed-wrap { + margin: 10px 0px; +} + +#litespeed-modal-deactivate .deactivate-clear-settings-wrapper, +#litespeed-modal-deactivate .deactivate-actions { + margin-top: 30px; +} + +#litespeed-modal-deactivate .deactivate-reason-wrapper label, +#litespeed-modal-deactivate .deactivate-clear-settings-wrapper label { + width: 100%; + display: block; + margin-bottom: 5px; +} + +#litespeed-modal-deactivate .deactivate-actions { + display: flex; + justify-content: space-between; +} \ No newline at end of file diff --git a/assets/js/lazyload.init.js b/assets/js/lazyload.init.js index 6448fc66a..71e032723 100644 --- a/assets/js/lazyload.init.js +++ b/assets/js/lazyload.init.js @@ -17,8 +17,17 @@ }; var init = function () { - console.log('[LiteSpeed] Start Lazy Load Images'); - instance = new LazyLoad({ elements_selector: '[data-lazyloaded]', callback_finish: litespeed_finish_callback }); + console.log('[LiteSpeed] Start Lazy Load'); + instance = new LazyLoad( + Object.assign( + {}, + window.lazyLoadOptions || {}, + { + elements_selector: '[data-lazyloaded]', + callback_finish: litespeed_finish_callback, + }, + ) + ); update_lazyload = function () { instance.update(); diff --git a/assets/js/lazyload.lib.js b/assets/js/lazyload.lib.js index e9beed69c..34efafe65 100644 --- a/assets/js/lazyload.lib.js +++ b/assets/js/lazyload.lib.js @@ -809,9 +809,9 @@ resetStatus(element); }; // Automatic instances creation if required (useful for async script loading) - if (runningOnBrowser) { - autoInitialize(LazyLoad, window.lazyLoadOptions); - } + // if (runningOnBrowser) { + // autoInitialize(LazyLoad, window.lazyLoadOptions); + // } return LazyLoad; }); diff --git a/assets/js/lazyload.min.js b/assets/js/lazyload.min.js index a1bcd1b69..9e2067b6a 100644 --- a/assets/js/lazyload.min.js +++ b/assets/js/lazyload.min.js @@ -1 +1 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).LazyLoad=e()}(this,function(){"use strict";function e(){return(e=Object.assign||function(t){for(var e=1;e__crawler = Crawler2::cls(); + $this->crawler = Crawler2::cls(); } /** - * List all crawler + * List all crawlers + * + * Displays a table of all crawlers with their details. * * ## OPTIONS * @@ -32,13 +51,17 @@ public function __construct() { * * # List all crawlers * $ wp litespeed-crawler l + * + * @since 1.1.0 */ public function l() { $this->list(); } /** - * List all crawler + * List all crawlers + * + * Displays a table of all crawlers with their details. * * ## OPTIONS * @@ -46,19 +69,21 @@ public function l() { * * # List all crawlers * $ wp litespeed-crawler list + * + * @since 1.1.0 */ public function list() { - $crawler_list = $this->__crawler->list_crawlers(); + $crawler_list = $this->crawler->list_crawlers(); $summary = Crawler2::get_summary(); if ($summary['curr_crawler'] >= count($crawler_list)) { $summary['curr_crawler'] = 0; } $is_running = time() - $summary['is_running'] <= 900; - $CRAWLER_RUN_INTERVAL = defined('LITESPEED_CRAWLER_RUN_INTERVAL') ? LITESPEED_CRAWLER_RUN_INTERVAL : 600; // Specify time in seconds for the time between each run interval - if ($CRAWLER_RUN_INTERVAL > 0) { + $crawler_run_interval = defined('LITESPEED_CRAWLER_RUN_INTERVAL') ? LITESPEED_CRAWLER_RUN_INTERVAL : 600; // Specify time in seconds for the time between each run interval + if ($crawler_run_interval > 0) { $recurrence = ''; - $hours = (int) floor($CRAWLER_RUN_INTERVAL / 3600); + $hours = (int) floor($crawler_run_interval / 3600); if ($hours) { if ($hours > 1) { $recurrence .= sprintf(__('%d hours', 'litespeed-cache'), $hours); @@ -66,7 +91,7 @@ public function list() { $recurrence .= sprintf(__('%d hour', 'litespeed-cache'), $hours); } } - $minutes = (int) floor(($CRAWLER_RUN_INTERVAL % 3600) / 60); + $minutes = (int) floor(($crawler_run_interval % 3600) / 60); if ($minutes) { $recurrence .= ' '; if ($minutes > 1) { @@ -86,7 +111,7 @@ public function list() { $blacklisted += !empty($summary['crawler_stats'][$i][Crawler2::STATUS_NOCACHE]) ? $summary['crawler_stats'][$i][Crawler2::STATUS_NOCACHE] : 0; if (isset($summary['crawler_stats'][$i][Crawler2::STATUS_WAIT])) { - $waiting = $summary['crawler_stats'][$i][Crawler2::STATUS_WAIT] ?: 0; + $waiting = $summary['crawler_stats'][$i][Crawler2::STATUS_WAIT] ?? 0; } else { $waiting = $summary['list_size'] - $hit - $miss - $blacklisted; } @@ -97,14 +122,14 @@ public function list() { $analytics .= ' Blocked: ' . $blacklisted; $running = ''; - if ($i == $summary['curr_crawler']) { + if ($i === $summary['curr_crawler']) { $running = 'Pos: ' . ($summary['last_pos'] + 1); if ($is_running) { $running .= '(Running)'; } } - $status = $this->__crawler->is_active($i) ? '✅' : '❌'; + $status = $this->crawler->is_active($i) ? '✅' : '❌'; $list[] = array( 'ID' => $i + 1, @@ -124,19 +149,25 @@ public function list() { * * ## OPTIONS * + * + * : The ID of the crawler to enable. + * * ## EXAMPLES * * # Turn on 2nd crawler * $ wp litespeed-crawler enable 2 + * + * @since 1.1.0 + * @param array $args Command arguments. */ public function enable( $args ) { $id = $args[0] - 1; - if ($this->__crawler->is_active($id)) { + if ($this->crawler->is_active($id)) { WP_CLI::error('ID #' . $id . ' had been enabled'); return; } - $this->__crawler->toggle_activeness($id); + $this->crawler->toggle_activeness($id); WP_CLI::success('Enabled crawler #' . $id); } @@ -145,19 +176,25 @@ public function enable( $args ) { * * ## OPTIONS * + * + * : The ID of the crawler to disable. + * * ## EXAMPLES * * # Turn off 1st crawler * $ wp litespeed-crawler disable 1 + * + * @since 1.1.0 + * @param array $args Command arguments. */ public function disable( $args ) { $id = $args[0] - 1; - if (!$this->__crawler->is_active($id)) { + if (!$this->crawler->is_active($id)) { WP_CLI::error('ID #' . $id . ' has been disabled'); return; } - $this->__crawler->toggle_activeness($id); + $this->crawler->toggle_activeness($id); WP_CLI::success('Disabled crawler #' . $id); } @@ -170,6 +207,8 @@ public function disable( $args ) { * * # Start crawling * $ wp litespeed-crawler r + * + * @since 1.1.0 */ public function r() { $this->run(); @@ -184,10 +223,12 @@ public function r() { * * # Start crawling * $ wp litespeed-crawler run + * + * @since 1.1.0 */ public function run() { self::debug('⚠️⚠️⚠️ Forced take over lane (CLI)'); - $this->__crawler->Release_lane(); + $this->crawler->Release_lane(); Task::async_call('crawler'); @@ -197,7 +238,7 @@ public function run() { } /** - * Reset position + * Reset crawler position * * ## OPTIONS * @@ -205,9 +246,11 @@ public function run() { * * # Reset crawler position * $ wp litespeed-crawler reset + * + * @since 1.1.0 */ public function reset() { - $this->__crawler->reset_pos(); + $this->crawler->reset_pos(); $summary = Crawler2::get_summary(); diff --git a/cli/database.cls.php b/cli/database.cls.php new file mode 100644 index 000000000..c4ab68e8f --- /dev/null +++ b/cli/database.cls.php @@ -0,0 +1,227 @@ +db = DB_Optm::cls(); + } + + /** + * List all site domains and ids on the network. + */ + public function network_list() { + if ( !is_multisite() ) { + WP_CLI::error('This is not a multisite installation!'); + + return; + } + $buf = WP_CLI::colorize("%CThe list of installs:%n\n"); + + $sites = get_sites(); + foreach ( $sites as $site ) { + $buf .= WP_CLI::colorize( '%Y' . $site->domain . $site->path . ':%n ID ' . $site->blog_id ) . "\n"; + } + + WP_CLI::line($buf); + } + + /** + * Change to blog sent as param. + * + * @param array $args Description. + */ + private function change_to_blog( $args ) { + if ( !isset( $args[0] ) || 'blog' !== $args[0] ) { + return; + } + + $this->current_blog = get_current_blog_id(); + $blogid = $args[1]; + if ( !is_numeric( $blogid ) ) { + $error = WP_CLI::colorize( '%RError: invalid blog id entered.%n' ); + WP_CLI::line( $error ); + $this->network_list( $args ); + return; + } + $site = get_blog_details( $blogid ); + if ( false === $site ) { + $error = WP_CLI::colorize( '%RError: invalid blog id entered.%n' ); + WP_CLI::line( $error ); + $this->network_list( $args ); + return; + } + switch_to_blog( $blogid ); + } + + /** + * Change to previous blog. + */ + private function change_to_default() { + // Check if previous blog set. + if ( $this->current_blog ) { + switch_to_blog( $this->current_blog ); + // Switched to previous blog. + $this->current_blog = false; + } + } + + /** + * Show CLI response. + * + * @param boolean $result Flag if result is success or failure. + * @param string $action Action name. + */ + private function show_response( $result, $action ) { + if ($result) { + WP_CLI::success( $result ); + } else { + WP_CLI::error( 'Error running optimization: ' . $action ); + } + } + + /** + * Clean actions function. + * + * @param int $args Action arguments. + * @param array $types What data to clean. + */ + private function clean_action( $args, $types ) { + $this->change_to_blog( $args ); + foreach ( $types as $type ) { + $result = $this->db->handler_clean_db_cli( $type ); + $this->show_response( $result, $type ); + } + $this->change_to_default(); + } + + /** + * Clear posts data(revisions, orphaned, auto drafts, trashed posts). + * # Start clearing posts data. + * $ wp litespeed-database clear_posts + * $ wp litespeed-database clear_posts blog 2 + * + * @param string $args Action arguments. + */ + public function clear_posts( $args ) { + $types = array( + 'revision', + 'orphaned_post_meta', + 'auto_draft', + 'trash_post', + ); + $this->clean_action( $args, $types ); + } + + /** + * Clear comments(spam and trash comments). + * # Start clearing comments. + * $ wp litespeed-database clear_comments + * $ wp litespeed-database clear_comments blog 2 + * + * @param string $args Action arguments. + */ + public function clear_comments( $args ) { + $types = array( + 'spam_comment', + 'trash_comment', + ); + $this->clean_action( $args, $types ); + } + + /** + * Clear trackbacks/pingbacks. + * # Start clearing trackbacks/pingbacks. + * $ wp litespeed-database clear_trackbacks + * $ wp litespeed-database clear_trackbacks blog 2 + * + * @param string $args Action arguments. + */ + public function clear_trackbacks( $args ) { + $types = array( + 'trackback-pingback', + ); + $this->clean_action( $args, $types ); + } + + /** + * Clear transients. + * # Start clearing transients. + * $ wp litespeed-database clear_transients + * $ wp litespeed-database clear_transients blog 2 + * + * @param string $args Action arguments. + */ + public function clear_transients( $args ) { + $types = array( + 'expired_transient', + 'all_transients', + ); + $this->clean_action( $args, $types ); + } + + /** + * Optimize tables. + * # Start optimizing tables. + * $ wp litespeed-database optimize_tables + * $ wp litespeed-database optimize_tables blog 2 + * + * @param string $args Action arguments. + */ + public function optimize_tables( $args ) { + $types = array( + 'optimize_tables', + ); + $this->clean_action( $args, $types ); + } + + /** + * Optimize database by running all possible operations. + * # Start optimizing all. + * $ wp litespeed-database optimize_all + * $ wp litespeed-database optimize_all blog 2 + * + * @param string $args Action arguments. + */ + public function optimize_all( $args ) { + $types = array( + 'all', + ); + $this->clean_action( $args, $types ); + } +} diff --git a/cli/debug.cls.php b/cli/debug.cls.php index 48cba6121..d74dd8b43 100644 --- a/cli/debug.cls.php +++ b/cli/debug.cls.php @@ -1,7 +1,13 @@ __report = Report::cls(); + $this->report = Report::cls(); } /** @@ -31,7 +45,7 @@ public function __construct() { * $ wp litespeed-debug send */ public function send() { - $num = $this->__report->post_env(); - WP_CLI::success('Report Number = ' . $num); + $num = $this->report->post_env(); + WP_CLI::success( 'Report Number = ' . $num ); } } diff --git a/cli/image.cls.php b/cli/image.cls.php index 2362e11d3..f1e7cb00a 100644 --- a/cli/image.cls.php +++ b/cli/image.cls.php @@ -1,8 +1,13 @@ __img_optm = Img_Optm::cls(); + $this->img_optm = Img_Optm::cls(); } /** - * Batch toggle optimized images w/ original images + * Batch toggle optimized images with original images. * * ## OPTIONS * + * [] + * : Type to switch to (orig or optm). + * * ## EXAMPLES * * # Switch to original images @@ -35,14 +51,16 @@ public function __construct() { * * # Switch to optimized images * $ wp litespeed-image batch_switch optm + * + * @param array $param Positional arguments (type). */ public function batch_switch( $param ) { $type = $param[0]; - $this->__img_optm->batch_switch($type); + $this->img_optm->batch_switch( $type ); } /** - * Send image optimization request to QUIC.cloud server + * Send image optimization request to QUIC.cloud server. * * ## OPTIONS * @@ -52,11 +70,11 @@ public function batch_switch( $param ) { * $ wp litespeed-image push */ public function push() { - $this->__img_optm->new_req(); + $this->img_optm->new_req(); } /** - * Pull optimized images from QUIC.cloud server + * Pull optimized images from QUIC.cloud server. * * ## OPTIONS * @@ -66,11 +84,11 @@ public function push() { * $ wp litespeed-image pull */ public function pull() { - $this->__img_optm->pull(true); + $this->img_optm->pull( true ); } /** - * Show optimization status based on local data + * Show optimization status based on local data (alias for status). * * ## OPTIONS * @@ -84,7 +102,7 @@ public function s() { } /** - * Show optimization status based on local data + * Show optimization status based on local data. * * ## OPTIONS * @@ -95,55 +113,55 @@ public function s() { */ public function status() { $summary = Img_Optm::get_summary(); - $img_count = $this->__img_optm->img_count(); - foreach (Lang::img_status() as $k => $v) { - if (isset($img_count["img.$k"])) { + $img_count = $this->img_optm->img_count(); + foreach ( Lang::img_status() as $k => $v ) { + if ( isset( $img_count["img.$k"] ) ) { $img_count["$v - images"] = $img_count["img.$k"]; - unset($img_count["img.$k"]); + unset( $img_count["img.$k"] ); } - if (isset($img_count["group.$k"])) { + if ( isset( $img_count["group.$k"] ) ) { $img_count["$v - groups"] = $img_count["group.$k"]; - unset($img_count["group.$k"]); + unset( $img_count["group.$k"] ); } } - foreach (array( 'reduced', 'reduced_webp', 'reduced_avif' ) as $v) { - if (!empty($summary[$v])) { - $summary[$v] = Utility::real_size($summary[$v]); + foreach ( array( 'reduced', 'reduced_webp', 'reduced_avif' ) as $v ) { + if ( ! empty( $summary[$v] ) ) { + $summary[$v] = Utility::real_size( $summary[$v] ); } } - if (!empty($summary['last_requested'])) { - $summary['last_requested'] = date('m/d/y H:i:s', $summary['last_requested']); + if ( ! empty( $summary['last_requested'] ) ) { + $summary['last_requested'] = gmdate( 'm/d/y H:i:s', $summary['last_requested'] ); } $list = array(); - foreach ($summary as $k => $v) { + foreach ( $summary as $k => $v ) { $list[] = array( - 'key' => $k, + 'key' => $k, 'value' => $v, ); } $list2 = array(); - foreach ($img_count as $k => $v) { - if (!$v) { + foreach ( $img_count as $k => $v ) { + if ( ! $v ) { continue; } $list2[] = array( - 'key' => $k, + 'key' => $k, 'value' => $v, ); } - WP_CLI\Utils\format_items('table', $list, array( 'key', 'value' )); + WP_CLI\Utils\format_items( 'table', $list, array( 'key', 'value' ) ); - WP_CLI::line(WP_CLI::colorize('%CImages in database summary:%n')); - WP_CLI\Utils\format_items('table', $list2, array( 'key', 'value' )); + WP_CLI::line( WP_CLI::colorize( '%CImages in database summary:%n' ) ); + WP_CLI\Utils\format_items( 'table', $list2, array( 'key', 'value' ) ); } /** - * Clean up unfinished image data from QUIC.cloud server + * Clean up unfinished image data from QUIC.cloud server. * * ## OPTIONS * @@ -153,15 +171,15 @@ public function status() { * $ wp litespeed-image clean */ public function clean() { - $this->__img_optm->clean(); + $this->img_optm->clean(); - WP_CLI::line(WP_CLI::colorize('%CLatest status:%n')); + WP_CLI::line( WP_CLI::colorize( '%CLatest status:%n' ) ); $this->status(); } /** - * Remove original image backups + * Remove original image backups. * * ## OPTIONS * @@ -171,6 +189,6 @@ public function clean() { * $ wp litespeed-image rm_bkup */ public function rm_bkup() { - $this->__img_optm->rm_bkup(); + $this->img_optm->rm_bkup(); } } diff --git a/cli/online.cls.php b/cli/online.cls.php index 4aa5400f2..10d1dd4d3 100644 --- a/cli/online.cls.php +++ b/cli/online.cls.php @@ -1,8 +1,13 @@ __cloud = Cloud::cls(); + $this->cloud = Cloud::cls(); } /** @@ -32,13 +45,13 @@ public function __construct() { * $ wp litespeed-online init */ public function init() { - $resp = $this->__cloud->init_qc_cli(); - if (!empty($resp['qc_activated'])) { - $main_domain = !empty($resp['main_domain']) ? $resp['main_domain'] : false; - $this->__cloud->update_qc_activation($resp['qc_activated'], $main_domain); - WP_CLI::success('Init successfully. Activated type: ' . $resp['qc_activated']); + $resp = $this->cloud->init_qc_cli(); + if ( ! empty( $resp['qc_activated'] ) ) { + $main_domain = ! empty( $resp['main_domain'] ) ? $resp['main_domain'] : false; + $this->cloud->update_qc_activation( $resp['qc_activated'], $main_domain ); + WP_CLI::success( 'Init successfully. Activated type: ' . $resp['qc_activated'] ); } else { - WP_CLI::error('Init failed!'); + WP_CLI::error( 'Init failed!' ); } } @@ -47,6 +60,21 @@ public function init() { * * ## OPTIONS * + * [--method=] + * : The method to use (e.g., cname, ns, cfi). + * + * [--ssl-cert=] + * : Path to SSL certificate. + * + * [--ssl-key=] + * : Path to SSL key. + * + * [--cf-token=] + * : Cloudflare token for CFI method. + * + * [--format=] + * : Output format (e.g., json). + * * ## EXAMPLES * * # Activate domain CDN on QUIC.cloud (support --format=json) @@ -54,46 +82,49 @@ public function init() { * $ wp litespeed-online cdn_init --method=cname|ns --ssl-cert=xxx.pem --ssl-key=xxx * $ wp litespeed-online cdn_init --method=cfi --cf-token=xxxxxxxx * $ wp litespeed-online cdn_init --method=cfi --cf-token=xxxxxxxx --ssl-cert=xxx.pem --ssl-key=xxx + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function cdn_init( $args, $assoc_args ) { - if (empty($assoc_args['method'])) { - WP_CLI::error('Init CDN failed! Missing parameters `--method`.'); + if ( empty( $assoc_args['method'] ) ) { + WP_CLI::error( 'Init CDN failed! Missing parameters `--method`.' ); return; } - if ((!empty($assoc_args['ssl-cert']) && empty($assoc_args['ssl-key'])) || (empty($assoc_args['ssl-cert']) && !empty($assoc_args['ssl-key']))) { - WP_CLI::error('Init CDN failed! SSL cert must be present together w/ SSL key.'); + if ( ( ! empty( $assoc_args['ssl-cert'] ) && empty( $assoc_args['ssl-key'] ) ) || ( empty( $assoc_args['ssl-cert'] ) && ! empty( $assoc_args['ssl-key'] ) ) ) { + WP_CLI::error( 'Init CDN failed! SSL cert must be present together w/ SSL key.' ); return; } - if ($assoc_args['method'] == 'cfi' && empty($assoc_args['cf-token'])) { - WP_CLI::error('Init CDN failed! CFI must set `--cf-token`.'); + if ( 'cfi' === $assoc_args['method'] && empty( $assoc_args['cf-token'] ) ) { + WP_CLI::error( 'Init CDN failed! CFI must set `--cf-token`.' ); return; } - $cert = !empty($assoc_args['ssl-cert']) ? $assoc_args['ssl-cert'] : ''; - $key = !empty($assoc_args['ssl-key']) ? $assoc_args['ssl-key'] : ''; - $cf_token = !empty($assoc_args['cf-token']) ? $assoc_args['cf-token'] : ''; + $cert = ! empty( $assoc_args['ssl-cert'] ) ? $assoc_args['ssl-cert'] : ''; + $key = ! empty( $assoc_args['ssl-key'] ) ? $assoc_args['ssl-key'] : ''; + $cf_token = ! empty( $assoc_args['cf-token'] ) ? $assoc_args['cf-token'] : ''; - $resp = $this->__cloud->init_qc_cdn_cli($assoc_args['method'], $cert, $key, $cf_token); - if (!empty($resp['qc_activated'])) { - $main_domain = !empty($resp['main_domain']) ? $resp['main_domain'] : false; - $this->__cloud->update_qc_activation($resp['qc_activated'], $main_domain, true); + $resp = $this->cloud->init_qc_cdn_cli( $assoc_args['method'], $cert, $key, $cf_token ); + if ( ! empty( $resp['qc_activated'] ) ) { + $main_domain = ! empty( $resp['main_domain'] ) ? $resp['main_domain'] : false; + $this->cloud->update_qc_activation( $resp['qc_activated'], $main_domain, true ); } - if (!empty($assoc_args['format']) && $assoc_args['format'] == 'json') { - WP_CLI::log(json_encode($resp)); + if ( ! empty( $assoc_args['format'] ) && 'json' === $assoc_args['format'] ) { + WP_CLI::log( wp_json_encode( $resp ) ); return; } - if (!empty($resp['qc_activated'])) { - WP_CLI::success('Init QC CDN successfully. Activated type: ' . $resp['qc_activated']); + if ( ! empty( $resp['qc_activated'] ) ) { + WP_CLI::success( 'Init QC CDN successfully. Activated type: ' . $resp['qc_activated'] ); } else { - WP_CLI::error('Init QC CDN failed!'); + WP_CLI::error( 'Init QC CDN failed!' ); } - if (!empty($resp['cname'])) { - WP_CLI::success('cname: ' . $resp['cname']); + if ( ! empty( $resp['cname'] ) ) { + WP_CLI::success( 'cname: ' . $resp['cname'] ); } - if (!empty($resp['msgs'])) { - WP_CLI::success('msgs: ' . var_export($resp['msgs'], true)); + if ( ! empty( $resp['msgs'] ) ) { + WP_CLI::success( 'msgs: ' . wp_json_encode( $resp['msgs'] ) ); } } @@ -102,25 +133,34 @@ public function cdn_init( $args, $assoc_args ) { * * ## OPTIONS * + * [--email=] + * : User email for QUIC.cloud account. + * + * [--api-key=] + * : API key for QUIC.cloud account. + * * ## EXAMPLES * * # Link user account by api key * $ wp litespeed-online link --email=xxx@example.com --api-key=xxxx + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function link( $args, $assoc_args ) { - if (empty($assoc_args['email']) || empty($assoc_args['api-key'])) { - WP_CLI::error('Link to QUIC.cloud failed! Missing parameters `--email` or `--api-key`.'); + if ( empty( $assoc_args['email'] ) || empty( $assoc_args['api-key'] ) ) { + WP_CLI::error( 'Link to QUIC.cloud failed! Missing parameters `--email` or `--api-key`.' ); return; } - $resp = $this->__cloud->link_qc_cli($assoc_args['email'], $assoc_args['api-key']); - if (!empty($resp['qc_activated'])) { - $main_domain = !empty($resp['main_domain']) ? $resp['main_domain'] : false; - $this->__cloud->update_qc_activation($resp['qc_activated'], $main_domain, true); - WP_CLI::success('Link successfully!'); - WP_CLI::log(json_encode($resp)); + $resp = $this->cloud->link_qc_cli( $assoc_args['email'], $assoc_args['api-key'] ); + if ( ! empty( $resp['qc_activated'] ) ) { + $main_domain = ! empty( $resp['main_domain'] ) ? $resp['main_domain'] : false; + $this->cloud->update_qc_activation( $resp['qc_activated'], $main_domain, true ); + WP_CLI::success( 'Link successfully!' ); + WP_CLI::log( wp_json_encode( $resp ) ); } else { - WP_CLI::error('Link failed!'); + WP_CLI::error( 'Link failed!' ); } } @@ -129,33 +169,39 @@ public function link( $args, $assoc_args ) { * * ## OPTIONS * + * [--format=] + * : Output format (e.g., json). + * * ## EXAMPLES * * # Sync QUIC.cloud service usage info * $ wp litespeed-online sync + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function sync( $args, $assoc_args ) { - $json = $this->__cloud->sync_usage(); + $json = $this->cloud->sync_usage(); - if (!empty($assoc_args['format'])) { - WP_CLI::print_value($json, $assoc_args); + if ( ! empty( $assoc_args['format'] ) ) { + WP_CLI::print_value( $json, $assoc_args ); return; } - WP_CLI::success('Sync successfully'); + WP_CLI::success( 'Sync successfully' ); $list = array(); - foreach (Cloud::$SERVICES as $v) { + foreach ( Cloud::$services as $v ) { $list[] = array( 'key' => $v, - 'used' => !empty($json['usage.' . $v]['used']) ? $json['usage.' . $v]['used'] : 0, - 'quota' => !empty($json['usage.' . $v]['quota']) ? $json['usage.' . $v]['quota'] : 0, - 'PayAsYouGo_Used' => !empty($json['usage.' . $v]['pag_used']) ? $json['usage.' . $v]['pag_used'] : 0, - 'PayAsYouGo_Balance' => !empty($json['usage.' . $v]['pag_bal']) ? $json['usage.' . $v]['pag_bal'] : 0, + 'used' => ! empty( $json['usage.' . $v]['used'] ) ? $json['usage.' . $v]['used'] : 0, + 'quota' => ! empty( $json['usage.' . $v]['quota'] ) ? $json['usage.' . $v]['quota'] : 0, + 'PayAsYouGo_Used' => ! empty( $json['usage.' . $v]['pag_used'] ) ? $json['usage.' . $v]['pag_used'] : 0, + 'PayAsYouGo_Balance' => ! empty( $json['usage.' . $v]['pag_bal'] ) ? $json['usage.' . $v]['pag_bal'] : 0, ); } - WP_CLI\Utils\format_items('table', $list, array( 'key', 'used', 'quota', 'PayAsYouGo_Used', 'PayAsYouGo_Balance' )); + WP_CLI\Utils\format_items( 'table', $list, array( 'key', 'used', 'quota', 'PayAsYouGo_Used', 'PayAsYouGo_Balance' ) ); } /** @@ -168,9 +214,9 @@ public function sync( $args, $assoc_args ) { * # Check QC account status * $ wp litespeed-online cdn_status */ - public function cdn_status( $args, $assoc_args ) { - $resp = $this->__cloud->cdn_status_cli(); - WP_CLI::log(json_encode($resp)); + public function cdn_status() { + $resp = $this->cloud->cdn_status_cli(); + WP_CLI::log( wp_json_encode( $resp ) ); } /** @@ -178,25 +224,31 @@ public function cdn_status( $args, $assoc_args ) { * * ## OPTIONS * + * [--format=] + * : Output format (e.g., json). + * * ## EXAMPLES * * # List all services tag * $ wp litespeed-online services + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function services( $args, $assoc_args ) { - if (!empty($assoc_args['format'])) { - WP_CLI::print_value(Cloud::$SERVICES, $assoc_args); + if ( ! empty( $assoc_args['format'] ) ) { + WP_CLI::print_value( Cloud::$services, $assoc_args ); return; } $list = array(); - foreach (Cloud::$SERVICES as $v) { + foreach ( Cloud::$services as $v ) { $list[] = array( 'service' => $v, ); } - WP_CLI\Utils\format_items('table', $list, array( 'service' )); + WP_CLI\Utils\format_items( 'table', $list, array( 'service' ) ); } /** @@ -204,18 +256,24 @@ public function services( $args, $assoc_args ) { * * ## OPTIONS * + * [--format=] + * : Output format (e.g., json). + * * ## EXAMPLES * * # List all QUIC.cloud servers in use * $ wp litespeed-online nodes + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function nodes( $args, $assoc_args ) { $json = Cloud::get_summary(); $list = array(); $json_output = array(); - foreach (Cloud::$SERVICES as $v) { - $server = !empty($json['server.' . $v]) ? $json['server.' . $v] : ''; + foreach ( Cloud::$services as $v ) { + $server = ! empty( $json['server.' . $v] ) ? $json['server.' . $v] : ''; $list[] = array( 'service' => $v, 'server' => $server, @@ -223,12 +281,12 @@ public function nodes( $args, $assoc_args ) { $json_output[] = array( $v => $server ); } - if (!empty($assoc_args['format'])) { - WP_CLI::print_value($json_output, $assoc_args); + if ( ! empty( $assoc_args['format'] ) ) { + WP_CLI::print_value( $json_output, $assoc_args ); return; } - WP_CLI\Utils\format_items('table', $list, array( 'service', 'server' )); + WP_CLI\Utils\format_items( 'table', $list, array( 'service', 'server' ) ); } /** @@ -236,21 +294,30 @@ public function nodes( $args, $assoc_args ) { * * ## OPTIONS * + * [] + * : Service to ping (e.g., img_optm). + * + * [--force] + * : Force detection of the closest server. + * * ## EXAMPLES * * # Detect closest node for one service * $ wp litespeed-online ping img_optm * $ wp litespeed-online ping img_optm --force + * + * @param array $param Positional arguments (service). + * @param array $assoc_args Associative arguments. */ public function ping( $param, $assoc_args ) { $svc = $param[0]; - $force = !empty($assoc_args['force']); + $force = ! empty( $assoc_args['force'] ); - $json = $this->__cloud->detect_cloud($svc, $force); - if ($json) { - WP_CLI::success('Updated closest server.'); + $json = $this->cloud->detect_cloud( $svc, $force ); + if ( $json ) { + WP_CLI::success( 'Updated closest server.' ); } - WP_CLI::log('svc = ' . $svc); - WP_CLI::log('node = ' . ($json ?: '-')); + WP_CLI::log( 'svc = ' . $svc ); + WP_CLI::log( 'node = ' . ( $json ? $json : '-' ) ); } } diff --git a/cli/option.cls.php b/cli/option.cls.php index 23f5d59de..e5c71a61b 100644 --- a/cli/option.cls.php +++ b/cli/option.cls.php @@ -1,12 +1,19 @@ cls('Admin_Settings')->save($raw_data); - WP_CLI::line("$key:"); - $this->get($args, $assoc_args); + $this->cls( 'Admin_Settings' )->save( $raw_data ); + WP_CLI::line( "$key:" ); + $this->get( $args, $assoc_args ); } /** - * Get the plugin options. + * Get all plugin options. * * ## OPTIONS * + * [--format=] + * : Output format (e.g., json). + * * ## EXAMPLES * * # Get all options * $ wp litespeed-option all * $ wp litespeed-option all --json + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function all( $args, $assoc_args ) { $options = $this->get_options(); - if (!empty($assoc_args['format'])) { - WP_CLI::print_value($options, $assoc_args); + if ( ! empty( $assoc_args['format'] ) ) { + WP_CLI::print_value( $options, $assoc_args ); return; } $option_out = array(); - $buf = WP_CLI::colorize('%CThe list of options:%n'); - WP_CLI::line($buf); + $buf = WP_CLI::colorize( '%CThe list of options:%n' ); + WP_CLI::line( $buf ); - foreach ($options as $k => $v) { - if ($k == self::O_CDN_MAPPING || $k == self::O_CRAWLER_COOKIES) { - foreach ($v as $k2 => $v2) { + foreach ( $options as $k => $v ) { + if ( self::O_CDN_MAPPING === $k || self::O_CRAWLER_COOKIES === $k ) { + foreach ( $v as $k2 => $v2 ) { // $k2 is numeric - if (is_array($v2)) { - foreach ($v2 as $k3 => $v3) { - // $k3 = 'url/inc_img/name/vals' - if (is_array($v3)) { + if ( is_array( $v2 ) ) { + foreach ( $v2 as $k3 => $v3 ) { + // $k3 is 'url/inc_img/name/vals' + if ( is_array( $v3 ) ) { $option_out[] = array( - 'key' => '', + 'key' => '', 'value' => '', ); - foreach ($v3 as $k4 => $v4) { + foreach ( $v3 as $k4 => $v4 ) { $option_out[] = array( - 'key' => $k4 == 0 ? "{$k}[$k3][$k2]" : '', + 'key' => 0 === $k4 ? "{$k}[$k3][$k2]" : '', 'value' => $v4, ); } $option_out[] = array( - 'key' => '', + 'key' => '', 'value' => '', ); } else { $option_out[] = array( - 'key' => "{$k}[$k3][$k2]", + 'key' => "{$k}[$k3][$k2]", 'value' => $v3, ); } @@ -136,106 +146,109 @@ public function all( $args, $assoc_args ) { } } continue; - } elseif (is_array($v) && $v) { - // $v = implode( PHP_EOL, $v ); + } elseif ( is_array( $v ) && $v ) { $option_out[] = array( - 'key' => '', + 'key' => '', 'value' => '', ); - foreach ($v as $k2 => $v2) { + foreach ( $v as $k2 => $v2 ) { $option_out[] = array( - 'key' => $k2 == 0 ? $k : '', + 'key' => 0 === $k2 ? $k : '', 'value' => $v2, ); } $option_out[] = array( - 'key' => '', + 'key' => '', 'value' => '', ); continue; } - if (array_key_exists($k, self::$_default_options) && is_bool(self::$_default_options[$k]) && !$v) { + if ( array_key_exists( $k, self::$_default_options ) && is_bool( self::$_default_options[ $k ] ) && ! $v ) { $v = 0; } - if ($v === '' || $v === array()) { + if ( '' === $v || array() === $v ) { $v = "''"; } $option_out[] = array( - 'key' => $k, + 'key' => $k, 'value' => $v, ); } - WP_CLI\Utils\format_items('table', $option_out, array( 'key', 'value' )); + WP_CLI\Utils\format_items( 'table', $option_out, array( 'key', 'value' ) ); } /** - * Get the plugin options. + * Get a specific plugin option. * * ## OPTIONS * + * + * : The option ID to retrieve (e.g., cache-priv, cdn-mapping[url][0]). + * * ## EXAMPLES * * # Get one option * $ wp litespeed-option get cache-priv * $ wp litespeed-option get 'cdn-mapping[url][0]' + * + * @param array $args Positional arguments (id). + * @param array $assoc_args Associative arguments. */ public function get( $args, $assoc_args ) { $id = $args[0]; $child = false; - if (strpos($id, '[')) { - parse_str($id, $id2); + if ( false !== strpos( $id, '[' ) ) { + parse_str( $id, $id2 ); Utility::compatibility(); - $id = array_key_first($id2); + $id = array_key_first( $id2 ); - $child = array_key_first($id2[$id]); // `url` - if (!$child) { - WP_CLI::error('Wrong child key'); + $child = array_key_first( $id2[ $id ] ); // is `url` + if ( ! $child ) { + WP_CLI::error( 'Wrong child key' ); return; } - $numeric = array_key_first($id2[$id][$child]); // `0` - if ($numeric === null) { - WP_CLI::error('Wrong 2nd level numeric key'); + $numeric = array_key_first( $id2[ $id ][ $child ] ); // `0` + if ( null === $numeric ) { + WP_CLI::error( 'Wrong 2nd level numeric key' ); return; } } - if (!isset(self::$_default_options[$id])) { - WP_CLI::error('ID not exist [id] ' . $id); + if ( ! isset( self::$_default_options[ $id ] ) ) { + WP_CLI::error( 'ID not exist [id] ' . $id ); return; } - $v = $this->conf($id); - $default_v = self::$_default_options[$id]; - - /** - * For CDN_mapping and crawler_cookies - * Examples of option name: - * cdn-mapping[url][0] - * crawler-cookies[name][1] - */ - if ($id == self::O_CDN_MAPPING) { - if (!in_array($child, array( self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE ))) { - WP_CLI::error('Wrong child key'); + $v = $this->conf( $id ); + $default_v = self::$_default_options[ $id ]; + + // For CDN_mapping and crawler_cookies + // Examples of option name: + // cdn-mapping[url][0] + // crawler-cookies[name][1] + if ( self::O_CDN_MAPPING === $id ) { + if ( ! in_array( $child, array( self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE ), true ) ) { + WP_CLI::error( 'Wrong child key' ); return; } } - if ($id == self::O_CRAWLER_COOKIES) { - if (!in_array($child, array( self::CRWL_COOKIE_NAME, self::CRWL_COOKIE_VALS ))) { - WP_CLI::error('Wrong child key'); + if ( self::O_CRAWLER_COOKIES === $id ) { + if ( ! in_array( $child, array( self::CRWL_COOKIE_NAME, self::CRWL_COOKIE_VALS ), true ) ) { + WP_CLI::error( 'Wrong child key' ); return; } } - if ($id == self::O_CDN_MAPPING || $id == self::O_CRAWLER_COOKIES) { - if (!empty($v[$numeric][$child])) { - $v = $v[$numeric][$child]; - } elseif ($id == self::O_CDN_MAPPING) { - if (in_array($child, array( self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS ))) { + if ( self::O_CDN_MAPPING === $id || self::O_CRAWLER_COOKIES === $id ) { + if ( ! empty( $v[ $numeric ][ $child ] ) ) { + $v = $v[ $numeric ][ $child ]; + } elseif ( self::O_CDN_MAPPING === $id ) { + if ( in_array( $child, array( self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS ), true ) ) { $v = 0; } else { $v = "''"; @@ -245,20 +258,20 @@ public function get( $args, $assoc_args ) { } } - if (is_array($v)) { - $v = implode(PHP_EOL, $v); + if ( is_array( $v ) ) { + $v = implode( PHP_EOL, $v ); } - if (!$v && $id != self::O_CDN_MAPPING && $id != self::O_CRAWLER_COOKIES) { + if ( ! $v && self::O_CDN_MAPPING !== $id && self::O_CRAWLER_COOKIES !== $id ) { // empty array for CDN/crawler has been handled - if (is_bool($default_v)) { + if ( is_bool( $default_v ) ) { $v = 0; - } elseif (!is_array($default_v)) { + } elseif ( ! is_array( $default_v ) ) { $v = "''"; } } - WP_CLI::line($v); + WP_CLI::line( $v ); } /** @@ -274,26 +287,36 @@ public function get( $args, $assoc_args ) { * * # Export options to a file. * $ wp litespeed-option export + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function export( $args, $assoc_args ) { - if (isset($assoc_args['filename'])) { + if ( isset( $assoc_args['filename'] ) ) { $file = $assoc_args['filename']; } else { - $file = getcwd() . '/litespeed_options_' . date('d_m_Y-His') . '.data'; + $file = getcwd() . '/litespeed_options_' . gmdate( 'd_m_Y-His' ) . '.data'; + } + + global $wp_filesystem; + if ( ! $wp_filesystem ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); } - if (!is_writable(dirname($file))) { - WP_CLI::error('Directory not writable.'); + if ( ! $wp_filesystem->is_writable( dirname( $file ) ) ) { + WP_CLI::error( 'Directory not writable.' ); return; } - $data = $this->cls('Import')->export(true); + $data = $this->cls( 'Import' )->export( true ); - if (file_put_contents($file, $data) === false) { - WP_CLI::error('Failed to create file.'); - } else { - WP_CLI::success('Created file ' . $file); + if ( false === $wp_filesystem->put_contents( $file, $data ) ) { + WP_CLI::error( 'Failed to create file.' ); + return; } + + WP_CLI::success( 'Created file ' . $file ); } /** @@ -302,7 +325,7 @@ public function export( $args, $assoc_args ) { * The file must be formatted as such: * option_key=option_value * One per line. - * A Semicolon at the beginning of the line indicates a comment and will be skipped. + * A semicolon at the beginning of the line indicates a comment and will be skipped. * * ## OPTIONS * @@ -313,20 +336,32 @@ public function export( $args, $assoc_args ) { * * # Import options from CURRENTDIR/options.txt * $ wp litespeed-option import options.txt + * + * @param array $args Positional arguments (file). + * @param array $assoc_args Associative arguments. */ public function import( $args, $assoc_args ) { $file = $args[0]; - if (!file_exists($file) || !is_readable($file)) { - WP_CLI::error('File does not exist or is not readable.'); + + global $wp_filesystem; + if ( ! $wp_filesystem ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); } - $res = $this->cls('Import')->import($file); + if ( ! $wp_filesystem->exists( $file ) || ! $wp_filesystem->is_readable( $file ) ) { + WP_CLI::error( 'File does not exist or is not readable.' ); + return; + } + + $res = $this->cls( 'Import' )->import( $file ); - if (!$res) { - WP_CLI::error('Failed to parse serialized data from file.'); + if ( ! $res ) { + WP_CLI::error( 'Failed to parse serialized data from file.' ); + return; } - WP_CLI::success('Options imported. [File] ' . $file); + WP_CLI::success( 'Options imported. [File] ' . $file ); } /** @@ -335,7 +370,7 @@ public function import( $args, $assoc_args ) { * The file must be formatted as such: * option_key=option_value * One per line. - * A Semicolon at the beginning of the line indicates a comment and will be skipped. + * A semicolon at the beginning of the line indicates a comment and will be skipped. * * ## OPTIONS * @@ -346,24 +381,27 @@ public function import( $args, $assoc_args ) { * * # Import options from https://domain.com/options.txt * $ wp litespeed-option import_remote https://domain.com/options.txt + * + * @param array $args Positional arguments (url). */ - public function import_remote( $args, $assoc_args ) { + public function import_remote( $args ) { $file = $args[0]; - $tmp_file = download_url($file); + $tmp_file = download_url( $file ); - if (is_wp_error($tmp_file)) { - WP_CLI::error('Failed to download file.'); + if ( is_wp_error( $tmp_file ) ) { + WP_CLI::error( 'Failed to download file.' ); return; } - $res = $this->cls('Import')->import($tmp_file); + $res = $this->cls( 'Import' )->import( $tmp_file ); - if (!$res) { - WP_CLI::error('Failed to parse serialized data from file.'); + if ( ! $res ) { + WP_CLI::error( 'Failed to parse serialized data from file.' ); + return; } - WP_CLI::success('Options imported. [File] ' . $file); + WP_CLI::success( 'Options imported. [File] ' . $file ); } /** @@ -375,6 +413,6 @@ public function import_remote( $args, $assoc_args ) { * $ wp litespeed-option reset */ public function reset() { - $this->cls('Import')->reset(); + $this->cls( 'Import' )->reset(); } } diff --git a/cli/presets.cls.php b/cli/presets.cls.php index 215a3812f..57e421d49 100644 --- a/cli/presets.cls.php +++ b/cli/presets.cls.php @@ -1,7 +1,13 @@ __preset = Preset::cls(); + $this->preset = Preset::cls(); } /** @@ -26,20 +39,25 @@ public function __construct() { * * ## OPTIONS * + * + * : The preset name to apply (e.g., basic). + * * ## EXAMPLES * * # Apply the preset called "basic" * $ wp litespeed-presets apply basic + * + * @param array $args Positional arguments (preset). */ public function apply( $args ) { $preset = $args[0]; - if (!isset($preset)) { - WP_CLI::error('Please specify a preset to apply.'); + if ( empty( $preset ) ) { + WP_CLI::error( 'Please specify a preset to apply.' ); return; } - return $this->__preset->apply($preset); + return $this->preset->apply( $preset ); } /** @@ -53,10 +71,10 @@ public function apply( $args ) { * $ wp litespeed-presets get_backups */ public function get_backups() { - $backups = $this->__preset->get_backups(); + $backups = $this->preset->get_backups(); - foreach ($backups as $backup) { - WP_CLI::line($backup); + foreach ( $backups as $backup ) { + WP_CLI::line( $backup ); } } @@ -65,19 +83,24 @@ public function get_backups() { * * ## OPTIONS * + * + * : The timestamp of the backup to restore. + * * ## EXAMPLES * * # Restore the backup with the timestamp 1667485245 * $ wp litespeed-presets restore 1667485245 + * + * @param array $args Positional arguments (timestamp). */ public function restore( $args ) { $timestamp = $args[0]; - if (!isset($timestamp)) { - WP_CLI::error('Please specify a timestamp to restore.'); + if ( empty( $timestamp ) ) { + WP_CLI::error( 'Please specify a timestamp to restore.' ); return; } - return $this->__preset->restore($timestamp); + return $this->preset->restore( $timestamp ); } } diff --git a/cli/purge.cls.php b/cli/purge.cls.php index 4e53f958f..be40c0eda 100644 --- a/cli/purge.cls.php +++ b/cli/purge.cls.php @@ -1,7 +1,13 @@ domain . $site->path . ':%n ID ' . $site->blog_id) . "\n"; - } + $buf = WP_CLI::colorize( '%CThe list of installs:%n' ) . PHP_EOL; + + $sites = get_sites(); + foreach ( $sites as $site ) { + $buf .= WP_CLI::colorize( '%Y' . $site->domain . $site->path . ':%n ID ' . $site->blog_id ) . PHP_EOL; } - WP_CLI::line($buf); + WP_CLI::line( $buf ); } /** - * Sends an ajax request to the site. Takes an action and the nonce string to perform. + * Sends an AJAX request to the site. * + * @param string $action The action to perform. + * @param array $extra Additional data to include in the request. + * @return object The HTTP response. * @since 1.0.14 */ - private function _send_request( $action, $extra = array() ) { + private function send_request( $action, $extra = array() ) { $data = array( Router::ACTION => $action, - Router::NONCE => wp_create_nonce($action), + Router::NONCE => wp_create_nonce( $action ), ); - if (!empty($extra)) { - $data = array_merge($data, $extra); + if ( ! empty( $extra ) ) { + $data = array_merge( $data, $extra ); } - $url = admin_url('admin-ajax.php'); - WP_CLI::debug('URL is ' . $url); + $url = admin_url( 'admin-ajax.php' ); + WP_CLI::debug( 'URL is ' . $url ); - $out = WP_CLI\Utils\http_request('GET', $url, $data); + $out = WP_CLI\Utils\http_request( 'GET', $url, $data ); return $out; } @@ -75,19 +77,15 @@ private function _send_request( $action, $extra = array() ) { * # Purge Everything associated with the WordPress install. * $ wp litespeed-purge all */ - public function all( $args ) { - if (is_multisite()) { - $action = Core::ACTION_QS_PURGE_EMPTYCACHE; - } else { - $action = Core::ACTION_QS_PURGE_ALL; - } + public function all() { + $action = is_multisite() ? Core::ACTION_QS_PURGE_EMPTYCACHE : Core::ACTION_QS_PURGE_ALL; - $purge_ret = $this->_send_request($action); + $purge_ret = $this->send_request( $action ); - if ($purge_ret->success) { - WP_CLI::success(__('Purged All!', 'litespeed-cache')); + if ( $purge_ret->success ) { + WP_CLI::success( __( 'Purged All!', 'litespeed-cache' ) ); } else { - WP_CLI::error('Something went wrong! Got ' . $purge_ret->status_code); + WP_CLI::error( 'Something went wrong! Got ' . $purge_ret->status_code ); } } @@ -97,193 +95,204 @@ public function all( $args ) { * ## OPTIONS * * - * : The blog id to purge + * : The blog id to purge. * * ## EXAMPLES * * # In a multisite install, purge only the shop.example.com cache (stored as blog id 2). * $ wp litespeed-purge blog 2 + * + * @param array $args Positional arguments (blogid). */ public function blog( $args ) { - if (!is_multisite()) { - WP_CLI::error('Not a multisite installation.'); + if ( ! is_multisite() ) { + WP_CLI::error( 'Not a multisite installation.' ); return; } + $blogid = $args[0]; - if (!is_numeric($blogid)) { - $error = WP_CLI::colorize('%RError: invalid blog id entered.%n'); - WP_CLI::line($error); - $this->network_list($args); + if ( ! is_numeric( $blogid ) ) { + $error = WP_CLI::colorize( '%RError: invalid blog id entered.%n' ); + WP_CLI::line( $error ); + $this->network_list( $args ); return; } - $site = get_blog_details($blogid); - if ($site === false) { - $error = WP_CLI::colorize('%RError: invalid blog id entered.%n'); - WP_CLI::line($error); - $this->network_list($args); + + $site = get_blog_details( $blogid ); + if ( false === $site ) { + $error = WP_CLI::colorize( '%RError: invalid blog id entered.%n' ); + WP_CLI::line( $error ); + $this->network_list( $args ); return; } - switch_to_blog($blogid); - $purge_ret = $this->_send_request(Core::ACTION_QS_PURGE_ALL); - if ($purge_ret->success) { - WP_CLI::success(__('Purged the blog!', 'litespeed-cache')); + switch_to_blog( $blogid ); + + $purge_ret = $this->send_request( Core::ACTION_QS_PURGE_ALL ); + if ( $purge_ret->success ) { + WP_CLI::success( __( 'Purged the blog!', 'litespeed-cache' ) ); } else { - WP_CLI::error('Something went wrong! Got ' . $purge_ret->status_code); + WP_CLI::error( 'Something went wrong! Got ' . $purge_ret->status_code ); } } /** - * Purges all cache tags related to a url. + * Purges all cache tags related to a URL. * * ## OPTIONS * * - * : The url to purge. + * : The URL to purge. * * ## EXAMPLES * * # Purge the front page. * $ wp litespeed-purge url https://mysite.com/ + * + * @param array $args Positional arguments (URL). */ public function url( $args ) { $data = array( Router::ACTION => Core::ACTION_QS_PURGE, ); $url = $args[0]; - $deconstructed = wp_parse_url($url); - if (empty($deconstructed)) { - WP_CLI::error('url passed in is invalid.'); + $deconstructed = wp_parse_url( $url ); + if ( empty( $deconstructed ) ) { + WP_CLI::error( 'URL passed in is invalid.' ); return; } - if (is_multisite()) { - if (get_blog_id_from_url($deconstructed['host'], '/') === 0) { - WP_CLI::error('Multisite url passed in is invalid.'); + if ( is_multisite() ) { + if ( 0 === get_blog_id_from_url( $deconstructed['host'], '/' ) ) { + WP_CLI::error( 'Multisite URL passed in is invalid.' ); return; } } else { - $deconstructed_site = wp_parse_url(get_home_url()); - if ($deconstructed['host'] !== $deconstructed_site['host']) { - WP_CLI::error('Single site url passed in is invalid.'); + $deconstructed_site = wp_parse_url( get_home_url() ); + if ( $deconstructed['host'] !== $deconstructed_site['host'] ) { + WP_CLI::error( 'Single site URL passed in is invalid.' ); return; } } - WP_CLI::debug('url is ' . $url); + WP_CLI::debug( 'URL is ' . $url ); - $purge_ret = WP_CLI\Utils\http_request('GET', $url, $data); - if ($purge_ret->success) { - WP_CLI::success(__('Purged the url!', 'litespeed-cache')); + $purge_ret = WP_CLI\Utils\http_request( 'GET', $url, $data ); + if ( $purge_ret->success ) { + WP_CLI::success( __( 'Purged the URL!', 'litespeed-cache' ) ); } else { - WP_CLI::error('Something went wrong! Got ' . $purge_ret->status_code); + WP_CLI::error( 'Something went wrong! Got ' . $purge_ret->status_code ); } } /** - * Helper function for purging by ids. + * Helper function for purging by IDs. * - * @access private - * @since 1.0.15 - * @param array $args The id list to parse. - * @param string $select The purge by kind - * @param function(int $id) $callback The callback function to check the id. + * @param array $args The ID list to parse. + * @param string $select The purge by kind. + * @param callable $callback The callback function to check the ID. */ - private function _purgeby( $args, $select, $callback ) { + private function purgeby( $args, $select, $callback ) { $filtered = array(); - foreach ($args as $val) { - if (!ctype_digit($val)) { - WP_CLI::debug('[LSCACHE] Skip val, not a number. ' . $val); + foreach ( $args as $val ) { + if ( ! ctype_digit( $val ) ) { + WP_CLI::debug( '[LSCACHE] Skip val, not a number. ' . $val ); continue; } - $term = $callback($val); - if (!empty($term)) { - WP_CLI::line($term->name); - $filtered[] = in_array($callback, array( 'get_tag', 'get_category' )) ? $term->name : $val; + $term = $callback( $val ); + if ( ! empty( $term ) ) { + WP_CLI::line( $term->name ); + $filtered[] = in_array( $callback, array( 'get_tag', 'get_category' ), true ) ? $term->name : $val; } else { - WP_CLI::debug('[LSCACHE] Skip val, not a valid term. ' . $val); + WP_CLI::debug( '[LSCACHE] Skip val, not a valid term. ' . $val ); } } - if (empty($filtered)) { - WP_CLI::error('Arguments must be integer ids.'); + if ( empty( $filtered ) ) { + WP_CLI::error( 'Arguments must be integer IDs.' ); return; } - $str = implode(',', $filtered); + $str = implode( ',', $filtered ); $purge_titles = array( - 0 => 'Category', - 1 => 'Post ID', - 2 => 'Tag', - 3 => 'URL', + Admin_Display::PURGEBY_CAT => 'Category', + Admin_Display::PURGEBY_PID => 'Post ID', + Admin_Display::PURGEBY_TAG => 'Tag', + Admin_Display::PURGEBY_URL => 'URL', ); - WP_CLI::line('Will purge the following: [' . $purge_titles[$select] . '] ' . $str); + WP_CLI::line( 'Will purge the following: [' . $purge_titles[ $select ] . '] ' . $str ); $data = array( Admin_Display::PURGEBYOPT_SELECT => $select, - Admin_Display::PURGEBYOPT_LIST => $str, + Admin_Display::PURGEBYOPT_LIST => $str, ); - $purge_ret = $this->_send_request(Core::ACTION_PURGE_BY, $data); - if ($purge_ret->success) { - WP_CLI::success(__('Purged!', 'litespeed-cache')); + $purge_ret = $this->send_request( Core::ACTION_PURGE_BY, $data ); + if ( $purge_ret->success ) { + WP_CLI::success( __( 'Purged!', 'litespeed-cache' ) ); } else { - WP_CLI::error('Something went wrong! Got ' . $purge_ret->status_code); + WP_CLI::error( 'Something went wrong! Got ' . $purge_ret->status_code ); } } /** - * Purges cache tags for a WordPress tag + * Purges cache tags for a WordPress tag. * * ## OPTIONS * * ... - * : the Term IDs to purge. + * : The Term IDs to purge. * * ## EXAMPLES * - * # Purge the tag ids 1, 3, and 5 + * # Purge the tag IDs 1, 3, and 5 * $ wp litespeed-purge tag 1 3 5 + * + * @param array $args Positional arguments (IDs). */ public function tag( $args ) { - $this->_purgeby($args, Admin_Display::PURGEBY_TAG, 'get_tag'); + $this->purgeby( $args, Admin_Display::PURGEBY_TAG, 'get_tag' ); } /** - * Purges cache tags for a WordPress category + * Purges cache tags for a WordPress category. * * ## OPTIONS * * ... - * : the Term IDs to purge. + * : The Term IDs to purge. * * ## EXAMPLES * - * # Purge the category ids 1, 3, and 5 + * # Purge the category IDs 1, 3, and 5 * $ wp litespeed-purge category 1 3 5 + * + * @param array $args Positional arguments (IDs). */ public function category( $args ) { - $this->_purgeby($args, Admin_Display::PURGEBY_CAT, 'get_category'); + $this->purgeby( $args, Admin_Display::PURGEBY_CAT, 'get_category' ); } /** - * Purges cache tags for a WordPress Post/Product + * Purges cache tags for a WordPress Post/Product. * * @alias product * * ## OPTIONS * * ... - * : the Post IDs to purge. + * : The Post IDs to purge. * * ## EXAMPLES * - * # Purge the post ids 1, 3, and 5 + * # Purge the post IDs 1, 3, and 5 * $ wp litespeed-purge post_id 1 3 5 + * + * @param array $args Positional arguments (IDs). */ public function post_id( $args ) { - $this->_purgeby($args, Admin_Display::PURGEBY_PID, 'get_post'); + $this->purgeby( $args, Admin_Display::PURGEBY_PID, 'get_post' ); } } diff --git a/composer.json b/composer.json index 61296a000..0bf26e4b2 100644 --- a/composer.json +++ b/composer.json @@ -6,12 +6,12 @@ "wp-coding-standards/wpcs": "^3.1", "phpcsstandards/phpcsutils": "^1.0", "phpcsstandards/phpcsextra": "^1.2", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "php-stubs/wp-cli-stubs": "^2.12" }, "prefer-stable": true, "scripts": { - "sniff-check": "vendor/bin/phpcs --standard=phpcs.ruleset.xml --no-cache tpl/ autoload.php", - "sniff-check-bk": "vendor/bin/phpcs --standard=phpcs.ruleset.xml --no-cache cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php" + "sniff-check": "vendor/bin/phpcs --standard=phpcs.ruleset.xml --no-cache cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php" }, "config": { "allow-plugins": { diff --git a/composer.lock b/composer.lock index 69ea5ff0f..01ca097bd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2a2890dfb714dc3008032c719ed83af7", + "content-hash": "8c6cb907d697cb733facab6d72af1add", "packages": [], "packages-dev": [ { @@ -85,6 +85,101 @@ }, "time": "2023-01-05T11:28:13+00:00" }, + { + "name": "php-stubs/wordpress-stubs", + "version": "v6.8.1", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "92e444847d94f7c30f88c60004648f507688acd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/92e444847d94f7c30f88c60004648f507688acd5", + "reference": "92e444847d94f7c30f88c60004648f507688acd5", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^5.4", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.8.3", + "phpdocumentor/reflection-docblock": "^5.4.1", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.5", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "paragonie/sodium_compat": "Pure PHP implementation of libsodium", + "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wordpress-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.8.1" + }, + "time": "2025-05-02T12:33:34+00:00" + }, + { + "name": "php-stubs/wp-cli-stubs", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wp-cli-stubs.git", + "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wp-cli-stubs/zipball/af16401e299a3fd2229bd0fa9a037638a4174a9d", + "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0" + }, + "require-dev": { + "php": "~7.3 || ~8.0", + "php-stubs/generator": "^0.8.0" + }, + "suggest": { + "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WP-CLI function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wp-cli-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress", + "wp-cli" + ], + "support": { + "issues": "https://github.com/php-stubs/wp-cli-stubs/issues", + "source": "https://github.com/php-stubs/wp-cli-stubs/tree/v2.12.0" + }, + "time": "2025-06-10T09:58:05+00:00" + }, { "name": "phpcompatibility/php-compatibility", "version": "9.3.5", diff --git a/lang/litespeed-cache.pot b/lang/litespeed-cache.pot index 77303930e..782c88b5d 100644 --- a/lang/litespeed-cache.pot +++ b/lang/litespeed-cache.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPLv3. msgid "" msgstr "" -"Project-Id-Version: LiteSpeed Cache 7.2\n" +"Project-Id-Version: LiteSpeed Cache 7.5\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/litespeed-cache\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2025-06-18T13:05:51+00:00\n" +"POT-Creation-Date: 2025-09-10T14:42:22+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.11.0\n" "X-Domain: litespeed-cache\n" @@ -19,7 +19,8 @@ msgstr "" #: tpl/banner/new_version.php:57 #: tpl/banner/new_version_dev.tpl.php:21 #: tpl/cache/more_settings_tip.tpl.php:28 -#: tpl/inc/admin_footer.php:11 +#: tpl/esi_widget_edit.php:41 +#: tpl/inc/admin_footer.php:17 msgid "LiteSpeed Cache" msgstr "" @@ -43,358 +44,398 @@ msgstr "" msgid "https://www.litespeedtech.com" msgstr "" -#: cli/crawler.cls.php:64 -#: tpl/crawler/summary.tpl.php:30 +#: cli/crawler.cls.php:89 +#: tpl/crawler/summary.tpl.php:39 msgid "%d hours" msgstr "" -#: cli/crawler.cls.php:66 -#: tpl/crawler/summary.tpl.php:32 +#: cli/crawler.cls.php:91 +#: tpl/crawler/summary.tpl.php:39 msgid "%d hour" msgstr "" -#: cli/crawler.cls.php:73 -#: tpl/crawler/summary.tpl.php:39 +#: cli/crawler.cls.php:98 +#: tpl/crawler/summary.tpl.php:47 msgid "%d minutes" msgstr "" -#: cli/crawler.cls.php:75 -#: tpl/crawler/summary.tpl.php:41 +#: cli/crawler.cls.php:100 +#: tpl/crawler/summary.tpl.php:47 msgid "%d minute" msgstr "" -#: cli/purge.cls.php:88 +#: cli/purge.cls.php:86 msgid "Purged All!" msgstr "" -#: cli/purge.cls.php:130 +#: cli/purge.cls.php:133 msgid "Purged the blog!" msgstr "" -#: cli/purge.cls.php:177 -msgid "Purged the url!" +#: cli/purge.cls.php:182 +msgid "Purged the URL!" msgstr "" -#: cli/purge.cls.php:231 +#: cli/purge.cls.php:234 msgid "Purged!" msgstr "" -#: src/activation.cls.php:494 -#: src/activation.cls.php:499 +#: src/activation.cls.php:563 +#: src/activation.cls.php:568 msgid "Failed to upgrade." msgstr "" -#: src/activation.cls.php:503 +#: src/activation.cls.php:572 msgid "Upgraded successfully." msgstr "" -#: src/admin-display.cls.php:122 -#: tpl/dash/entry.tpl.php:7 +#: src/admin-display.cls.php:250 +#: tpl/dash/entry.tpl.php:16 msgid "Dashboard" msgstr "" -#: src/admin-display.cls.php:124 +#: src/admin-display.cls.php:251 +#: src/lang.cls.php:269 +msgid "OptimaX" +msgstr "" + +#: src/admin-display.cls.php:252 msgid "Presets" msgstr "" -#: src/admin-display.cls.php:126 +#: src/admin-display.cls.php:253 msgid "General" msgstr "" -#: src/admin-display.cls.php:128 +#: src/admin-display.cls.php:254 +#: src/admin-display.cls.php:264 #: tpl/cache/entry.tpl.php:16 #: tpl/cache/entry_network.tpl.php:16 msgid "Cache" msgstr "" -#: src/admin-display.cls.php:130 +#: src/admin-display.cls.php:255 msgid "CDN" msgstr "" -#: src/admin-display.cls.php:132 -#: src/gui.cls.php:629 -#: tpl/dash/dashboard.tpl.php:190 -#: tpl/dash/network_dash.tpl.php:28 -#: tpl/general/online.tpl.php:121 -#: tpl/general/online.tpl.php:136 -#: tpl/presets/standard.tpl.php:25 +#: src/admin-display.cls.php:256 +#: src/gui.cls.php:727 +#: tpl/dash/dashboard.tpl.php:203 +#: tpl/dash/network_dash.tpl.php:36 +#: tpl/general/online.tpl.php:75 +#: tpl/general/online.tpl.php:134 +#: tpl/general/online.tpl.php:149 +#: tpl/presets/standard.tpl.php:32 msgid "Image Optimization" msgstr "" -#: src/admin-display.cls.php:134 -#: tpl/dash/dashboard.tpl.php:191 -#: tpl/dash/network_dash.tpl.php:29 -#: tpl/general/online.tpl.php:120 -#: tpl/general/online.tpl.php:135 +#: src/admin-display.cls.php:257 +#: tpl/dash/dashboard.tpl.php:204 +#: tpl/dash/network_dash.tpl.php:37 +#: tpl/general/online.tpl.php:83 +#: tpl/general/online.tpl.php:133 +#: tpl/general/online.tpl.php:148 msgid "Page Optimization" msgstr "" -#: src/admin-display.cls.php:136 +#: src/admin-display.cls.php:258 msgid "Database" msgstr "" -#: src/admin-display.cls.php:138 -#: src/lang.cls.php:248 +#: src/admin-display.cls.php:259 +#: src/lang.cls.php:249 msgid "Crawler" msgstr "" -#: src/admin-display.cls.php:140 +#: src/admin-display.cls.php:260 msgid "Toolbox" msgstr "" -#: src/admin-display.cls.php:214 +#: src/admin-display.cls.php:451 msgid "Cookie Name" msgstr "" -#: src/admin-display.cls.php:215 -#: tpl/crawler/settings.tpl.php:144 +#: src/admin-display.cls.php:452 +#: tpl/crawler/settings.tpl.php:179 msgid "Cookie Values" msgstr "" -#: src/admin-display.cls.php:217 +#: src/admin-display.cls.php:454 msgid "Remove cookie simulation" msgstr "" -#: src/admin-display.cls.php:218 +#: src/admin-display.cls.php:455 msgid "Add new cookie to simulate" msgstr "" -#: src/admin-display.cls.php:237 +#: src/admin-display.cls.php:478 msgid "CDN URL to be used. For example, %s" msgstr "" -#: src/admin-display.cls.php:239 +#: src/admin-display.cls.php:480 msgid "Remove CDN URL" msgstr "" -#: src/admin-display.cls.php:240 +#: src/admin-display.cls.php:481 msgid "Add new CDN URL" msgstr "" -#: src/admin-display.cls.php:241 -#: src/admin-display.cls.php:943 -#: src/admin-display.cls.php:970 -#: src/admin-display.cls.php:1019 -#: src/doc.cls.php:40 +#: src/admin-display.cls.php:482 +#: src/admin-display.cls.php:1163 +#: src/admin-display.cls.php:1193 +#: src/admin-display.cls.php:1275 +#: src/doc.cls.php:39 #: tpl/cache/settings-cache.tpl.php:28 #: tpl/cache/settings_inc.cache_mobile.tpl.php:91 -#: tpl/cdn/other.tpl.php:34 -#: tpl/crawler/settings.tpl.php:114 -#: tpl/page_optm/settings_css.tpl.php:221 -#: tpl/page_optm/settings_media.tpl.php:166 -#: tpl/toolbox/settings-debug.tpl.php:49 +#: tpl/cdn/other.tpl.php:45 +#: tpl/crawler/settings.tpl.php:138 +#: tpl/dash/dashboard.tpl.php:67 +#: tpl/dash/dashboard.tpl.php:459 +#: tpl/dash/dashboard.tpl.php:581 +#: tpl/dash/dashboard.tpl.php:610 +#: tpl/page_optm/settings_css.tpl.php:220 +#: tpl/page_optm/settings_media.tpl.php:176 +#: tpl/toolbox/settings-debug.tpl.php:80 msgid "ON" msgstr "" -#: src/admin-display.cls.php:242 -#: src/admin-display.cls.php:944 -#: src/admin-display.cls.php:970 -#: src/admin-display.cls.php:1019 +#: src/admin-display.cls.php:483 +#: src/admin-display.cls.php:1164 +#: src/admin-display.cls.php:1193 +#: src/admin-display.cls.php:1275 #: tpl/cache/settings-cache.tpl.php:28 #: tpl/cache/settings_inc.object.tpl.php:280 -#: tpl/cdn/other.tpl.php:39 -#: tpl/img_optm/settings.media_webp.tpl.php:14 -#: tpl/page_optm/settings_css.tpl.php:87 -#: tpl/page_optm/settings_js.tpl.php:69 -#: tpl/page_optm/settings_media.tpl.php:170 -#: tpl/toolbox/settings-debug.tpl.php:49 +#: tpl/cdn/other.tpl.php:53 +#: tpl/dash/dashboard.tpl.php:69 +#: tpl/dash/dashboard.tpl.php:461 +#: tpl/dash/dashboard.tpl.php:583 +#: tpl/dash/dashboard.tpl.php:612 +#: tpl/img_optm/settings.media_webp.tpl.php:22 +#: tpl/page_optm/settings_css.tpl.php:93 +#: tpl/page_optm/settings_js.tpl.php:77 +#: tpl/page_optm/settings_media.tpl.php:180 +#: tpl/toolbox/settings-debug.tpl.php:80 msgid "OFF" msgstr "" -#: src/admin-display.cls.php:290 -#: src/gui.cls.php:620 -#: tpl/crawler/entry.tpl.php:11 +#: src/admin-display.cls.php:544 +#: src/gui.cls.php:718 +#: tpl/crawler/entry.tpl.php:17 msgid "Settings" msgstr "" -#: src/admin-display.cls.php:518 +#: src/admin-display.cls.php:801 #: tpl/banner/new_version.php:114 -#: tpl/banner/score.php:140 +#: tpl/banner/score.php:142 #: tpl/banner/slack.php:49 msgid "Dismiss" msgstr "" -#: src/admin-display.cls.php:813 -#: src/admin-display.cls.php:817 +#: src/admin-display.cls.php:994 +#: src/admin-display.cls.php:999 msgid "Save Changes" msgstr "" -#: src/admin-display.cls.php:1030 -msgid "This setting is overwritten by the PHP constant %s" +#: src/admin-display.cls.php:1289 +msgid "This value is overwritten by the %s variable." +msgstr "" + +#: src/admin-display.cls.php:1293 +msgid "This value is overwritten by the filter." msgstr "" -#: src/admin-display.cls.php:1032 -msgid "This setting is overwritten by the primary site setting" +#: src/admin-display.cls.php:1296 +msgid "This value is overwritten by the PHP constant %s." msgstr "" -#: src/admin-display.cls.php:1034 -msgid "This setting is overwritten by the Network setting" +#: src/admin-display.cls.php:1300 +msgid "This value is overwritten by the primary site setting." msgstr "" -#: src/admin-display.cls.php:1037 -msgid "currently set to %s" +#: src/admin-display.cls.php:1302 +msgid "This value is overwritten by the Network setting." msgstr "" -#: src/admin-display.cls.php:1047 +#: src/admin-display.cls.php:1306 +msgid "Currently set to %s" +msgstr "" + +#: src/admin-display.cls.php:1319 +msgid "Value from filter applied" +msgstr "" + +#: src/admin-display.cls.php:1333 #: tpl/cache/settings_inc.object.tpl.php:162 -#: tpl/crawler/settings.tpl.php:37 -#: tpl/esi_widget_edit.php:70 +#: tpl/crawler/settings.tpl.php:43 +#: tpl/esi_widget_edit.php:78 msgid "seconds" msgstr "" -#: src/admin-display.cls.php:1078 -#: src/admin-display.cls.php:1082 -#: tpl/cdn/other.tpl.php:84 +#: src/admin-display.cls.php:1359 +#: src/admin-display.cls.php:1378 +#: tpl/cdn/other.tpl.php:108 msgid "Default value" msgstr "" -#: src/admin-display.cls.php:1106 +#: src/admin-display.cls.php:1406 msgid "Invalid rewrite rule" msgstr "" -#: src/admin-display.cls.php:1123 +#: src/admin-display.cls.php:1426 msgid "Path must end with %s" msgstr "" -#: src/admin-display.cls.php:1141 +#: src/admin-display.cls.php:1446 msgid "Minimum value" msgstr "" -#: src/admin-display.cls.php:1144 +#: src/admin-display.cls.php:1449 msgid "Maximum value" msgstr "" -#: src/admin-display.cls.php:1156 +#: src/admin-display.cls.php:1461 msgid "Zero, or" msgstr "" -#: src/admin-display.cls.php:1162 +#: src/admin-display.cls.php:1467 msgid "Larger than" msgstr "" -#: src/admin-display.cls.php:1164 +#: src/admin-display.cls.php:1469 msgid "Smaller than" msgstr "" -#: src/admin-display.cls.php:1167 +#: src/admin-display.cls.php:1472 msgid "Value range" msgstr "" -#: src/admin-display.cls.php:1192 +#: src/admin-display.cls.php:1500 msgid "Invalid IP" msgstr "" -#: src/admin-display.cls.php:1212 -#: tpl/cache/settings-esi.tpl.php:105 -#: tpl/page_optm/settings_css.tpl.php:224 -#: tpl/page_optm/settings_html.tpl.php:123 -#: tpl/page_optm/settings_media.tpl.php:249 -#: tpl/page_optm/settings_media_exc.tpl.php:27 -#: tpl/page_optm/settings_tuning.tpl.php:39 -#: tpl/page_optm/settings_tuning.tpl.php:59 -#: tpl/page_optm/settings_tuning.tpl.php:80 -#: tpl/page_optm/settings_tuning.tpl.php:101 -#: tpl/page_optm/settings_tuning.tpl.php:120 -#: tpl/page_optm/settings_tuning_css.tpl.php:25 -#: tpl/page_optm/settings_tuning_css.tpl.php:86 -#: tpl/toolbox/edit_htaccess.tpl.php:58 -#: tpl/toolbox/edit_htaccess.tpl.php:76 +#: src/admin-display.cls.php:1520 +#: tpl/cache/settings-esi.tpl.php:103 +#: tpl/page_optm/settings_css.tpl.php:87 +#: tpl/page_optm/settings_css.tpl.php:223 +#: tpl/page_optm/settings_html.tpl.php:131 +#: tpl/page_optm/settings_media.tpl.php:258 +#: tpl/page_optm/settings_media_exc.tpl.php:36 +#: tpl/page_optm/settings_tuning.tpl.php:48 +#: tpl/page_optm/settings_tuning.tpl.php:68 +#: tpl/page_optm/settings_tuning.tpl.php:89 +#: tpl/page_optm/settings_tuning.tpl.php:110 +#: tpl/page_optm/settings_tuning.tpl.php:129 +#: tpl/page_optm/settings_tuning_css.tpl.php:35 +#: tpl/page_optm/settings_tuning_css.tpl.php:96 +#: tpl/page_optm/settings_tuning_css.tpl.php:99 +#: tpl/page_optm/settings_tuning_css.tpl.php:100 +#: tpl/toolbox/edit_htaccess.tpl.php:61 +#: tpl/toolbox/edit_htaccess.tpl.php:79 msgid "API" msgstr "" -#: src/admin-display.cls.php:1214 +#. translators: %s: list of server variables in tags +#: src/admin-display.cls.php:1523 msgid "Server variable(s) %s available to override this setting." msgstr "" -#: src/admin-display.cls.php:1226 +#: src/admin-display.cls.php:1539 msgid "The URLs will be compared to the REQUEST_URI server variable." msgstr "" -#: src/admin-display.cls.php:1227 +#. translators: 1: example URL, 2: pattern example +#: src/admin-display.cls.php:1541 msgid "For example, for %1$s, %2$s can be used here." msgstr "" -#: src/admin-display.cls.php:1229 +#. translators: %s: caret symbol +#: src/admin-display.cls.php:1544 msgid "To match the beginning, add %s to the beginning of the item." msgstr "" -#: src/admin-display.cls.php:1230 +#. translators: %s: dollar symbol +#: src/admin-display.cls.php:1546 msgid "To do an exact match, add %s to the end of the URL." msgstr "" -#: src/admin-display.cls.php:1231 -#: src/doc.cls.php:109 +#: src/admin-display.cls.php:1547 +#: src/doc.cls.php:108 msgid "One per line." msgstr "" -#: src/admin-display.cls.php:1245 +#: src/admin-display.cls.php:1564 msgid "%s groups" msgstr "" -#: src/admin-display.cls.php:1248 +#: src/admin-display.cls.php:1567 msgid "%s images" msgstr "" -#: src/admin-display.cls.php:1257 +#: src/admin-display.cls.php:1576 msgid "%s group" msgstr "" -#: src/admin-display.cls.php:1260 +#: src/admin-display.cls.php:1579 msgid "%s image" msgstr "" -#: src/admin-settings.cls.php:92 +#: src/admin-settings.cls.php:40 +#: src/admin-settings.cls.php:313 +msgid "No fields" +msgstr "" + +#: src/admin-settings.cls.php:104 msgid "The user with id %s has editor access, which is not allowed for the role simulator." msgstr "" -#: src/admin-settings.cls.php:274 -#: src/admin-settings.cls.php:308 +#: src/admin-settings.cls.php:297 +#: src/admin-settings.cls.php:333 msgid "Options saved." msgstr "" -#: src/cdn/cloudflare.cls.php:111 +#: src/cdn/cloudflare.cls.php:121 msgid "Notified Cloudflare to set development mode to %s successfully." msgstr "" -#: src/cdn/cloudflare.cls.php:139 +#: src/cdn/cloudflare.cls.php:151 msgid "Cloudflare API is set to off." msgstr "" -#: src/cdn/cloudflare.cls.php:155 +#: src/cdn/cloudflare.cls.php:167 msgid "Notified Cloudflare to purge all successfully." msgstr "" -#: src/cdn/cloudflare.cls.php:169 +#: src/cdn/cloudflare.cls.php:181 msgid "No available Cloudflare zone" msgstr "" -#: src/cdn/cloudflare.cls.php:259 -#: src/cdn/cloudflare.cls.php:281 +#: src/cdn/cloudflare.cls.php:275 +#: src/cdn/cloudflare.cls.php:297 msgid "Failed to communicate with Cloudflare" msgstr "" -#: src/cdn/cloudflare.cls.php:272 +#: src/cdn/cloudflare.cls.php:288 msgid "Communicated with Cloudflare successfully." msgstr "" -#: src/cloud.cls.php:167 +#: src/cloud.cls.php:170 #: src/cloud.cls.php:250 msgid "QUIC.cloud's access to your WP REST API seems to be blocked." msgstr "" -#: src/cloud.cls.php:177 +#: src/cloud.cls.php:180 #: src/cloud.cls.php:260 msgid "Failed to get echo data from WPAPI" msgstr "" -#: src/cloud.cls.php:235 +#: src/cloud.cls.php:238 #: src/cloud.cls.php:290 msgid "You need to set the %1$s first. Please use the command %2$s to set." msgstr "" -#: src/cloud.cls.php:236 +#: src/cloud.cls.php:239 #: src/cloud.cls.php:291 -#: src/lang.cls.php:86 +#: src/lang.cls.php:85 msgid "Server IP" msgstr "" @@ -441,437 +482,401 @@ msgstr "" msgid "Reset %s activation successfully." msgstr "" -#: src/cloud.cls.php:973 -#: src/cloud.cls.php:986 -#: src/cloud.cls.php:1024 -#: src/cloud.cls.php:1090 -#: src/cloud.cls.php:1231 +#: src/cloud.cls.php:978 +#: src/cloud.cls.php:991 +#: src/cloud.cls.php:1029 +#: src/cloud.cls.php:1095 +#: src/cloud.cls.php:1236 msgid "Cloud Error" msgstr "" -#: src/cloud.cls.php:1024 +#: src/cloud.cls.php:1029 msgid "No available Cloud Node after checked server load." msgstr "" -#: src/cloud.cls.php:1090 +#: src/cloud.cls.php:1095 msgid "No available Cloud Node." msgstr "" -#: src/cloud.cls.php:1185 +#: src/cloud.cls.php:1190 msgid "In order to use QC services, need a real domain name, cannot use an IP." msgstr "" -#: src/cloud.cls.php:1234 +#: src/cloud.cls.php:1239 msgid "Please try after %1$s for service %2$s." msgstr "" -#: src/cloud.cls.php:1399 -#: src/cloud.cls.php:1422 +#: src/cloud.cls.php:1435 +#: src/cloud.cls.php:1458 msgid "Failed to request via WordPress" msgstr "" -#: src/cloud.cls.php:1454 +#: src/cloud.cls.php:1490 msgid "Cloud server refused the current request due to unpulled images. Please pull the images first." msgstr "" -#: src/cloud.cls.php:1459 +#: src/cloud.cls.php:1495 msgid "Your domain_key has been temporarily blocklisted to prevent abuse. You may contact support at QUIC.cloud to learn more." msgstr "" -#: src/cloud.cls.php:1466 +#: src/cloud.cls.php:1502 msgid "Cloud server refused the current request due to rate limiting. Please try again later." msgstr "" -#: src/cloud.cls.php:1474 +#: src/cloud.cls.php:1510 msgid "Redetected node" msgstr "" -#: src/cloud.cls.php:1482 +#: src/cloud.cls.php:1518 msgid "We are working hard to improve your online service experience. The service will be unavailable while we work. We apologize for any inconvenience." msgstr "" -#: src/cloud.cls.php:1527 -#: src/cloud.cls.php:1535 +#: src/cloud.cls.php:1563 +#: src/cloud.cls.php:1571 msgid "Message from QUIC.cloud server" msgstr "" -#: src/cloud.cls.php:1543 +#: src/cloud.cls.php:1579 msgid "Good news from QUIC.cloud server" msgstr "" -#: src/cloud.cls.php:1553 +#: src/cloud.cls.php:1589 msgid "%1$s plugin version %2$s required for this action." msgstr "" -#: src/cloud.cls.php:1620 +#: src/cloud.cls.php:1656 msgid "Failed to communicate with QUIC.cloud server" msgstr "" -#: src/cloud.cls.php:1673 +#: src/cloud.cls.php:1709 msgid "Site not recognized. QUIC.cloud deactivated automatically. Please reactivate your QUIC.cloud account." msgstr "" -#: src/cloud.cls.php:1674 +#: src/cloud.cls.php:1710 msgid "Click here to proceed." msgstr "" -#: src/cloud.cls.php:1940 +#: src/cloud.cls.php:1977 msgid "Linked to QUIC.cloud preview environment, for testing purpose only." msgstr "" -#: src/cloud.cls.php:1992 +#: src/cloud.cls.php:2029 msgid "Sync QUIC.cloud status successfully." msgstr "" -#: src/cloud.cls.php:1999 +#: src/cloud.cls.php:2036 msgid "Sync credit allowance with Cloud Server successfully." msgstr "" -#: src/conf.cls.php:508 +#: src/conf.cls.php:509 msgid "Saving option failed. IPv4 only for %s." msgstr "" -#: src/conf.cls.php:682 +#: src/conf.cls.php:683 msgid "Changed setting successfully." msgstr "" -#: src/core.cls.php:327 +#: src/core.cls.php:334 msgid "Notified LiteSpeed Web Server to purge everything." msgstr "" -#: src/core.cls.php:332 +#: src/core.cls.php:339 msgid "Notified LiteSpeed Web Server to purge the list." msgstr "" -#: src/crawler-map.cls.php:279 +#: src/crawler-map.cls.php:280 msgid "Sitemap cleaned successfully" msgstr "" -#: src/crawler-map.cls.php:371 +#: src/crawler-map.cls.php:372 msgid "No valid sitemap parsed for crawler." msgstr "" -#: src/crawler-map.cls.php:376 +#: src/crawler-map.cls.php:377 msgid "Sitemap created successfully: %d items" msgstr "" -#: src/crawler.cls.php:144 +#: src/crawler.cls.php:145 msgid "Crawler disabled list is cleared! All crawlers are set to active! " msgstr "" -#: src/crawler.cls.php:229 +#: src/crawler.cls.php:230 msgid "Started async crawling" msgstr "" -#: src/crawler.cls.php:1216 +#: src/crawler.cls.php:1236 msgid "Guest" msgstr "" -#: src/crawler.cls.php:1387 +#: src/crawler.cls.php:1407 msgid "Manually added to blocklist" msgstr "" -#: src/crawler.cls.php:1390 +#: src/crawler.cls.php:1410 msgid "Previously existed in blocklist" msgstr "" -#: src/data.cls.php:220 +#: src/data.cls.php:214 msgid "The database has been upgrading in the background since %s. This message will disappear once upgrade is complete." msgstr "" -#: src/data.upgrade.func.php:231 -msgid "LiteSpeed Cache upgraded successfully. NOTE: Due to changes in this version, the settings %1$s and %2$s have been turned OFF. Please turn them back on manually and verify that your site layout is correct, and you have no JS errors." -msgstr "" - -#: src/data.upgrade.func.php:235 -#: src/lang.cls.php:147 -msgid "JS Combine" -msgstr "" - -#: src/data.upgrade.func.php:236 -msgid "JS Defer" -msgstr "" - -#: src/data.upgrade.func.php:238 -msgid "Click here to settings" -msgstr "" - -#: src/db-optm.cls.php:143 +#: src/db-optm.cls.php:142 msgid "Clean all successfully." msgstr "" -#: src/db-optm.cls.php:200 +#: src/db-optm.cls.php:199 msgid "Clean post revisions successfully." msgstr "" -#: src/db-optm.cls.php:204 +#: src/db-optm.cls.php:203 msgid "Clean orphaned post meta successfully." msgstr "" -#: src/db-optm.cls.php:208 +#: src/db-optm.cls.php:207 msgid "Clean auto drafts successfully." msgstr "" -#: src/db-optm.cls.php:212 +#: src/db-optm.cls.php:211 msgid "Clean trashed posts and pages successfully." msgstr "" -#: src/db-optm.cls.php:216 +#: src/db-optm.cls.php:215 msgid "Clean spam comments successfully." msgstr "" -#: src/db-optm.cls.php:220 +#: src/db-optm.cls.php:219 msgid "Clean trashed comments successfully." msgstr "" -#: src/db-optm.cls.php:224 +#: src/db-optm.cls.php:223 msgid "Clean trackbacks and pingbacks successfully." msgstr "" -#: src/db-optm.cls.php:228 +#: src/db-optm.cls.php:227 msgid "Clean expired transients successfully." msgstr "" -#: src/db-optm.cls.php:232 +#: src/db-optm.cls.php:231 msgid "Clean all transients successfully." msgstr "" -#: src/db-optm.cls.php:242 +#: src/db-optm.cls.php:241 msgid "Optimized all tables." msgstr "" -#: src/db-optm.cls.php:292 +#: src/db-optm.cls.php:291 msgid "Converted to InnoDB successfully." msgstr "" -#: src/doc.cls.php:39 +#: src/doc.cls.php:38 msgid "This setting is %1$s for certain qualifying requests due to %2$s!" msgstr "" -#: src/doc.cls.php:55 +#: src/doc.cls.php:54 msgid "This setting will regenerate crawler list and clear the disabled list!" msgstr "" -#: src/doc.cls.php:66 +#: src/doc.cls.php:65 msgid "This site utilizes caching in order to facilitate a faster response time and better user experience. Caching potentially stores a duplicate copy of every web page that is on display on this site. All cache files are temporary, and are never accessed by any third party, except as necessary to obtain technical support from the cache plugin vendor. Cache files expire on a schedule set by the site administrator, but may easily be purged by the admin before their natural expiration, if necessary. We may use QUIC.cloud services to process & cache your data temporarily." msgstr "" -#: src/doc.cls.php:71 +#: src/doc.cls.php:70 msgid "Please see %s for more details." msgstr "" -#: src/doc.cls.php:88 -#: src/doc.cls.php:140 -#: tpl/cdn/cf.tpl.php:139 -#: tpl/dash/dashboard.tpl.php:179 -#: tpl/dash/dashboard.tpl.php:831 -#: tpl/general/online.tpl.php:67 -#: tpl/general/online.tpl.php:79 -#: tpl/general/online.tpl.php:95 -#: tpl/img_optm/summary.tpl.php:51 -#: tpl/inc/check_cache_disabled.php:42 +#: src/doc.cls.php:87 +#: src/doc.cls.php:139 +#: tpl/dash/dashboard.tpl.php:186 +#: tpl/dash/dashboard.tpl.php:845 +#: tpl/general/online.tpl.php:81 +#: tpl/general/online.tpl.php:93 +#: tpl/general/online.tpl.php:109 +#: tpl/general/online.tpl.php:114 +#: tpl/img_optm/summary.tpl.php:59 +#: tpl/inc/check_cache_disabled.php:46 +#: tpl/page_optm/settings_media.tpl.php:301 msgid "Learn More" msgstr "" -#: src/doc.cls.php:124 +#: src/doc.cls.php:123 msgid "Both full and partial strings can be used." msgstr "" -#: src/doc.cls.php:126 +#: src/doc.cls.php:125 msgid "Both full URLs and partial strings can be used." msgstr "" -#: src/doc.cls.php:138 +#: src/doc.cls.php:137 msgid "This setting will edit the .htaccess file." msgstr "" #: src/doc.cls.php:153 -msgid "For online services to work correctly, you must allowlist all %s server IPs." -msgstr "" - -#: src/doc.cls.php:154 -msgid "Before generating key, please verify all IPs on this list are allowlisted" -msgstr "" - -#: src/doc.cls.php:155 -msgid "Current Online Server IPs" -msgstr "" - -#: src/doc.cls.php:168 msgid "The queue is processed asynchronously. It may take time." msgstr "" -#: src/error.cls.php:45 -msgid "The setting %s is currently enabled." -msgstr "" - -#: src/error.cls.php:48 -msgid "Click here to change." -msgstr "" - -#: src/error.cls.php:57 +#: src/error.cls.php:69 msgid "You will need to finish %s setup to use the online services." msgstr "" -#: src/error.cls.php:58 -#: tpl/crawler/settings.tpl.php:106 -#: tpl/crawler/settings.tpl.php:115 -#: tpl/crawler/summary.tpl.php:185 +#: src/error.cls.php:74 +#: tpl/crawler/settings.tpl.php:123 +#: tpl/crawler/settings.tpl.php:144 +#: tpl/crawler/summary.tpl.php:218 msgid "Click here to set." msgstr "" -#: src/error.cls.php:62 +#: src/error.cls.php:82 msgid "You have used all of your daily quota for today." msgstr "" -#: src/error.cls.php:67 -#: src/error.cls.php:80 +#: src/error.cls.php:87 +#: src/error.cls.php:100 msgid "Learn more or purchase additional quota." msgstr "" -#: src/error.cls.php:75 +#: src/error.cls.php:95 msgid "You have used all of your quota left for current service this month." msgstr "" -#: src/error.cls.php:88 +#: src/error.cls.php:108 msgid "You have too many requested images, please try again in a few minutes." msgstr "" -#: src/error.cls.php:92 +#: src/error.cls.php:112 msgid "You have images waiting to be pulled. Please wait for the automatic pull to complete, or pull them down manually now." msgstr "" -#: src/error.cls.php:96 +#: src/error.cls.php:116 msgid "The image list is empty." msgstr "" -#: src/error.cls.php:100 -msgid "Not enough parameters. Please check if the domain key is set correctly" +#: src/error.cls.php:120 +msgid "Not enough parameters. Please check if the QUIC.cloud connection is set correctly" msgstr "" -#: src/error.cls.php:104 +#: src/error.cls.php:124 msgid "There is proceeding queue not pulled yet." msgstr "" -#: src/error.cls.php:109 +#: src/error.cls.php:129 msgid "There is proceeding queue not pulled yet. Queue info: %s." msgstr "" -#: src/error.cls.php:115 +#: src/error.cls.php:135 msgid "The site is not a valid alias on QUIC.cloud." msgstr "" -#: src/error.cls.php:119 +#: src/error.cls.php:139 msgid "The site is not registered on QUIC.cloud." msgstr "" -#: src/error.cls.php:123 -msgid "The domain key is not correct. Please try to sync your domain key again." +#: src/error.cls.php:143 +msgid "The QUIC.cloud connection is not correct. Please try to sync your QUIC.cloud connection again." msgstr "" -#: src/error.cls.php:127 +#: src/error.cls.php:147 msgid "The current server is under heavy load." msgstr "" -#: src/error.cls.php:131 +#: src/error.cls.php:151 msgid "Online node needs to be redetected." msgstr "" -#: src/error.cls.php:135 +#: src/error.cls.php:155 msgid "Credits are not enough to proceed the current request." msgstr "" -#: src/error.cls.php:139 -#: src/error.cls.php:163 +#: src/error.cls.php:159 +#: src/error.cls.php:183 msgid "%s file not writable." msgstr "" -#: src/error.cls.php:147 +#: src/error.cls.php:167 msgid "Could not find %1$s in %2$s." msgstr "" -#: src/error.cls.php:151 +#: src/error.cls.php:171 msgid "Invalid login cookie. Please check the %s file." msgstr "" -#: src/error.cls.php:155 +#: src/error.cls.php:175 msgid "Failed to back up %s file, aborted changes." msgstr "" -#: src/error.cls.php:159 +#: src/error.cls.php:179 msgid "%s file not readable." msgstr "" -#: src/error.cls.php:167 +#: src/error.cls.php:187 msgid "Failed to get %s file contents." msgstr "" -#: src/error.cls.php:171 +#: src/error.cls.php:191 msgid "Failed to create table %1$s! SQL: %2$s." msgstr "" -#: src/error.cls.php:175 +#: src/error.cls.php:195 msgid "Crawler disabled by the server admin." msgstr "" -#: src/error.cls.php:179 +#: src/error.cls.php:199 msgid "Previous request too recent. Please try again later." msgstr "" -#: src/error.cls.php:184 +#: src/error.cls.php:204 msgid "Previous request too recent. Please try again after %s." msgstr "" -#: src/error.cls.php:190 +#: src/error.cls.php:210 msgid "Your application is waiting for approval." msgstr "" -#: src/error.cls.php:194 +#: src/error.cls.php:214 msgid "The callback validation to your domain failed due to hash mismatch." msgstr "" -#: src/error.cls.php:198 +#: src/error.cls.php:218 msgid "The callback validation to your domain failed. Please make sure there is no firewall blocking our servers." msgstr "" -#: src/error.cls.php:203 +#: src/error.cls.php:223 msgid "The callback validation to your domain failed. Please make sure there is no firewall blocking our servers. Response code: " msgstr "" -#: src/error.cls.php:208 +#: src/error.cls.php:228 msgid "Your domain has been forbidden from using our services due to a previous policy violation." msgstr "" -#: src/error.cls.php:212 +#: src/error.cls.php:232 msgid "You cannot remove this DNS zone, because it is still in use. Please update the domain's nameservers, then try to delete this zone again, otherwise your site will become inaccessible." msgstr "" -#: src/error.cls.php:219 +#: src/error.cls.php:239 msgid "Unknown error" msgstr "" -#: src/file.cls.php:132 +#: src/file.cls.php:133 msgid "Filename is empty!" msgstr "" -#: src/file.cls.php:141 +#: src/file.cls.php:142 msgid "Folder does not exist: %s" msgstr "" -#: src/file.cls.php:153 +#: src/file.cls.php:154 msgid "Can not create folder: %1$s. Error: %2$s" msgstr "" -#: src/file.cls.php:161 +#: src/file.cls.php:162 msgid "Folder is not writable: %s." msgstr "" -#: src/file.cls.php:167 -#: src/file.cls.php:171 +#: src/file.cls.php:168 +#: src/file.cls.php:172 msgid "File %s is not writable." msgstr "" -#: src/file.cls.php:178 +#: src/file.cls.php:179 msgid "Failed to write to %s." msgstr "" @@ -880,775 +885,787 @@ msgid "%1$s %2$s files left in queue" msgstr "" #: src/gui.cls.php:83 +#: tpl/inc/modal.deactivation.php:77 msgid "Cancel" msgstr "" -#: src/gui.cls.php:392 -#: src/gui.cls.php:407 +#: src/gui.cls.php:470 +#: src/gui.cls.php:485 msgid "Purge this page" msgstr "" -#: src/gui.cls.php:416 +#: src/gui.cls.php:494 msgid "Mark this page as " msgstr "" -#: src/gui.cls.php:428 +#: src/gui.cls.php:506 msgid "Forced cacheable" msgstr "" -#: src/gui.cls.php:439 +#: src/gui.cls.php:517 msgid "Non cacheable" msgstr "" -#: src/gui.cls.php:450 +#: src/gui.cls.php:528 msgid "Private cache" msgstr "" -#: src/gui.cls.php:461 +#: src/gui.cls.php:539 msgid "No optimization" msgstr "" -#: src/gui.cls.php:469 +#: src/gui.cls.php:547 msgid "More settings" msgstr "" -#: src/gui.cls.php:476 -#: src/gui.cls.php:484 -#: src/gui.cls.php:492 -#: src/gui.cls.php:501 -#: src/gui.cls.php:511 -#: src/gui.cls.php:521 -#: src/gui.cls.php:531 -#: src/gui.cls.php:541 -#: src/gui.cls.php:550 -#: src/gui.cls.php:560 +#: src/gui.cls.php:554 +#: src/gui.cls.php:562 #: src/gui.cls.php:570 +#: src/gui.cls.php:579 +#: src/gui.cls.php:589 +#: src/gui.cls.php:599 +#: src/gui.cls.php:609 +#: src/gui.cls.php:619 +#: src/gui.cls.php:628 #: src/gui.cls.php:638 -#: src/gui.cls.php:646 -#: src/gui.cls.php:654 -#: src/gui.cls.php:663 -#: src/gui.cls.php:673 -#: src/gui.cls.php:683 -#: src/gui.cls.php:693 -#: src/gui.cls.php:703 -#: src/gui.cls.php:712 -#: src/gui.cls.php:722 -#: src/gui.cls.php:732 -#: tpl/page_optm/settings_media.tpl.php:131 -#: tpl/toolbox/purge.tpl.php:30 -#: tpl/toolbox/purge.tpl.php:36 -#: tpl/toolbox/purge.tpl.php:44 -#: tpl/toolbox/purge.tpl.php:53 -#: tpl/toolbox/purge.tpl.php:62 -#: tpl/toolbox/purge.tpl.php:71 -#: tpl/toolbox/purge.tpl.php:80 -#: tpl/toolbox/purge.tpl.php:89 -#: tpl/toolbox/purge.tpl.php:98 -#: tpl/toolbox/purge.tpl.php:107 +#: src/gui.cls.php:648 +#: src/gui.cls.php:736 +#: src/gui.cls.php:744 +#: src/gui.cls.php:752 +#: src/gui.cls.php:761 +#: src/gui.cls.php:771 +#: src/gui.cls.php:781 +#: src/gui.cls.php:791 +#: src/gui.cls.php:801 +#: src/gui.cls.php:810 +#: src/gui.cls.php:820 +#: src/gui.cls.php:830 +#: tpl/page_optm/settings_media.tpl.php:141 +#: tpl/toolbox/purge.tpl.php:40 +#: tpl/toolbox/purge.tpl.php:47 +#: tpl/toolbox/purge.tpl.php:55 +#: tpl/toolbox/purge.tpl.php:64 +#: tpl/toolbox/purge.tpl.php:73 +#: tpl/toolbox/purge.tpl.php:82 +#: tpl/toolbox/purge.tpl.php:91 +#: tpl/toolbox/purge.tpl.php:100 +#: tpl/toolbox/purge.tpl.php:109 +#: tpl/toolbox/purge.tpl.php:117 msgid "Purge All" msgstr "" -#: src/gui.cls.php:484 -#: src/gui.cls.php:593 -#: src/gui.cls.php:646 +#: src/gui.cls.php:562 +#: src/gui.cls.php:691 +#: src/gui.cls.php:744 msgid "LSCache" msgstr "" -#: src/gui.cls.php:492 -#: src/gui.cls.php:654 -#: tpl/toolbox/purge.tpl.php:36 +#: src/gui.cls.php:570 +#: src/gui.cls.php:752 +#: tpl/toolbox/purge.tpl.php:47 msgid "CSS/JS Cache" msgstr "" -#: src/gui.cls.php:501 -#: src/gui.cls.php:663 -#: tpl/cdn/cf.tpl.php:91 -#: tpl/cdn/entry.tpl.php:9 +#: src/gui.cls.php:579 +#: src/gui.cls.php:761 +#: tpl/cdn/cf.tpl.php:96 +#: tpl/cdn/entry.tpl.php:15 msgid "Cloudflare" msgstr "" -#: src/gui.cls.php:511 -#: src/gui.cls.php:673 -#: src/lang.cls.php:113 -#: tpl/dash/dashboard.tpl.php:56 -#: tpl/dash/dashboard.tpl.php:613 -#: tpl/toolbox/purge.tpl.php:44 +#: src/gui.cls.php:589 +#: src/gui.cls.php:771 +#: src/lang.cls.php:112 +#: tpl/dash/dashboard.tpl.php:60 +#: tpl/dash/dashboard.tpl.php:603 +#: tpl/toolbox/purge.tpl.php:55 msgid "Object Cache" msgstr "" -#: src/gui.cls.php:521 -#: src/gui.cls.php:683 -#: tpl/toolbox/purge.tpl.php:53 +#: src/gui.cls.php:599 +#: src/gui.cls.php:781 +#: tpl/toolbox/purge.tpl.php:64 msgid "Opcode Cache" msgstr "" -#: src/gui.cls.php:550 -#: src/gui.cls.php:712 -#: tpl/toolbox/purge.tpl.php:80 +#: src/gui.cls.php:628 +#: src/gui.cls.php:810 +#: tpl/toolbox/purge.tpl.php:91 msgid "Localized Resources" msgstr "" -#: src/gui.cls.php:560 -#: src/gui.cls.php:722 -#: tpl/page_optm/settings_media.tpl.php:131 -#: tpl/toolbox/purge.tpl.php:89 +#: src/gui.cls.php:638 +#: src/gui.cls.php:820 +#: tpl/page_optm/settings_media.tpl.php:141 +#: tpl/toolbox/purge.tpl.php:100 msgid "LQIP Cache" msgstr "" -#: src/gui.cls.php:570 -#: src/gui.cls.php:732 -#: src/lang.cls.php:180 -#: tpl/presets/standard.tpl.php:43 -#: tpl/toolbox/purge.tpl.php:98 +#: src/gui.cls.php:648 +#: src/gui.cls.php:830 +#: src/lang.cls.php:179 +#: tpl/presets/standard.tpl.php:49 +#: tpl/toolbox/purge.tpl.php:109 msgid "Gravatar Cache" msgstr "" -#: src/gui.cls.php:593 +#: src/gui.cls.php:681 +msgid "Enable All Features" +msgstr "" + +#: src/gui.cls.php:691 msgid "LiteSpeed Cache Purge All" msgstr "" -#: src/gui.cls.php:612 -#: tpl/db_optm/entry.tpl.php:7 +#: src/gui.cls.php:710 +#: tpl/db_optm/entry.tpl.php:13 msgid "Manage" msgstr "" -#: src/gui.cls.php:751 -#: tpl/img_optm/summary.tpl.php:169 +#: src/gui.cls.php:849 +#: tpl/img_optm/summary.tpl.php:176 msgid "Remove all previous unfinished image optimization requests." msgstr "" -#: src/gui.cls.php:752 -#: tpl/img_optm/summary.tpl.php:171 +#: src/gui.cls.php:850 +#: tpl/img_optm/summary.tpl.php:178 msgid "Clean Up Unfinished Data" msgstr "" -#: src/gui.cls.php:770 +#: src/gui.cls.php:868 msgid "Install %s" msgstr "" -#: src/gui.cls.php:771 +#: src/gui.cls.php:869 msgid "Install Now" msgstr "" -#: src/gui.cls.php:790 +#: src/gui.cls.php:888 msgid "View version %3$s details or update now." msgstr "" -#: src/gui.cls.php:792 +#: src/gui.cls.php:890 msgid "View %1$s version %2$s details" msgstr "" -#: src/gui.cls.php:795 +#: src/gui.cls.php:893 msgid "Update %s now" msgstr "" -#: src/htaccess.cls.php:328 +#: src/htaccess.cls.php:325 msgid "Mobile Agent Rules" msgstr "" -#: src/htaccess.cls.php:787 +#: src/htaccess.cls.php:784 msgid "

Please add/replace the following codes into the beginning of %1$s:

%2$s" msgstr "" -#: src/img-optm.cls.php:343 +#: src/img-optm.cls.php:350 msgid "Pushed %1$s to Cloud server, accepted %2$s." msgstr "" -#: src/img-optm.cls.php:604 +#: src/img-optm.cls.php:618 msgid "Cleared %1$s invalid images." msgstr "" -#: src/img-optm.cls.php:661 +#: src/img-optm.cls.php:675 msgid "No valid image found in the current request." msgstr "" -#: src/img-optm.cls.php:686 +#: src/img-optm.cls.php:700 msgid "No valid image found by Cloud server in the current request." msgstr "" -#: src/img-optm.cls.php:876 +#: src/img-optm.cls.php:890 msgid "Started async image optimization request" msgstr "" -#: src/img-optm.cls.php:962 +#: src/img-optm.cls.php:976 msgid "Pull Cron is running" msgstr "" -#: src/img-optm.cls.php:1072 -#: src/img-optm.cls.php:1098 +#: src/img-optm.cls.php:1086 msgid "Some optimized image file(s) has expired and was cleared." msgstr "" -#: src/img-optm.cls.php:1115 +#: src/img-optm.cls.php:1101 msgid "Pulled WebP image md5 does not match the notified WebP image md5." msgstr "" -#: src/img-optm.cls.php:1144 +#: src/img-optm.cls.php:1130 msgid "Pulled AVIF image md5 does not match the notified AVIF image md5." msgstr "" -#: src/img-optm.cls.php:1179 +#: src/img-optm.cls.php:1165 msgid "One or more pulled images does not match with the notified image md5" msgstr "" -#: src/img-optm.cls.php:1371 +#: src/img-optm.cls.php:1357 msgid "Cleaned up unfinished data successfully." msgstr "" -#: src/img-optm.cls.php:1388 +#: src/img-optm.cls.php:1374 msgid "Reset image optimization counter successfully." msgstr "" -#: src/img-optm.cls.php:1472 +#: src/img-optm.cls.php:1458 msgid "Destroy all optimization data successfully." msgstr "" -#: src/img-optm.cls.php:1537 -#: src/img-optm.cls.php:1599 +#: src/img-optm.cls.php:1523 +#: src/img-optm.cls.php:1587 msgid "Rescanned successfully." msgstr "" -#: src/img-optm.cls.php:1599 +#: src/img-optm.cls.php:1587 msgid "Rescanned %d images successfully." msgstr "" -#: src/img-optm.cls.php:1665 +#: src/img-optm.cls.php:1653 msgid "Calculated backups successfully." msgstr "" -#: src/img-optm.cls.php:1757 +#: src/img-optm.cls.php:1745 msgid "Removed backups successfully." msgstr "" -#: src/img-optm.cls.php:1904 +#: src/img-optm.cls.php:1892 msgid "Switched images successfully." msgstr "" -#: src/img-optm.cls.php:2001 -#: src/img-optm.cls.php:2061 +#: src/img-optm.cls.php:1989 +#: src/img-optm.cls.php:2049 msgid "Switched to optimized file successfully." msgstr "" -#: src/img-optm.cls.php:2020 +#: src/img-optm.cls.php:2008 msgid "Disabled WebP file successfully." msgstr "" -#: src/img-optm.cls.php:2025 +#: src/img-optm.cls.php:2013 msgid "Enabled WebP file successfully." msgstr "" -#: src/img-optm.cls.php:2034 +#: src/img-optm.cls.php:2022 msgid "Disabled AVIF file successfully." msgstr "" -#: src/img-optm.cls.php:2039 +#: src/img-optm.cls.php:2027 msgid "Enabled AVIF file successfully." msgstr "" -#: src/img-optm.cls.php:2055 +#: src/img-optm.cls.php:2043 msgid "Restored original file successfully." msgstr "" -#: src/img-optm.cls.php:2111 +#: src/img-optm.cls.php:2099 msgid "Reset the optimized data successfully." msgstr "" -#: src/import.cls.php:78 +#: src/import.cls.php:81 msgid "Import failed due to file error." msgstr "" -#: src/import.cls.php:131 +#: src/import.cls.php:134 msgid "Imported setting file %s successfully." msgstr "" -#: src/import.cls.php:153 +#: src/import.cls.php:156 msgid "Reset successfully." msgstr "" -#: src/lang.cls.php:25 +#: src/lang.cls.php:24 msgid "Images not requested" msgstr "" -#: src/lang.cls.php:26 +#: src/lang.cls.php:25 msgid "Images ready to request" msgstr "" -#: src/lang.cls.php:27 -#: tpl/dash/dashboard.tpl.php:555 +#: src/lang.cls.php:26 +#: tpl/dash/dashboard.tpl.php:551 msgid "Images requested" msgstr "" -#: src/lang.cls.php:28 -#: tpl/dash/dashboard.tpl.php:565 +#: src/lang.cls.php:27 +#: tpl/dash/dashboard.tpl.php:560 msgid "Images notified to pull" msgstr "" -#: src/lang.cls.php:29 +#: src/lang.cls.php:28 msgid "Images optimized and pulled" msgstr "" -#: src/lang.cls.php:47 +#: src/lang.cls.php:46 msgid "Unable to automatically add %1$s as a Domain Alias for main %2$s domain, due to potential CDN conflict." msgstr "" -#: src/lang.cls.php:52 +#: src/lang.cls.php:51 msgid "Unable to automatically add %1$s as a Domain Alias for main %2$s domain." msgstr "" -#: src/lang.cls.php:54 +#: src/lang.cls.php:53 msgid "Alias is in use by another QUIC.cloud account." msgstr "" -#: src/lang.cls.php:87 +#: src/lang.cls.php:86 msgid "Guest Mode User Agents" msgstr "" -#: src/lang.cls.php:88 +#: src/lang.cls.php:87 msgid "Guest Mode IPs" msgstr "" -#: src/lang.cls.php:90 +#: src/lang.cls.php:89 msgid "Enable Cache" msgstr "" -#: src/lang.cls.php:91 -#: tpl/dash/dashboard.tpl.php:57 -#: tpl/dash/dashboard.tpl.php:614 -#: tpl/presets/standard.tpl.php:13 +#: src/lang.cls.php:90 +#: tpl/dash/dashboard.tpl.php:61 +#: tpl/dash/dashboard.tpl.php:604 +#: tpl/presets/standard.tpl.php:21 msgid "Browser Cache" msgstr "" -#: src/lang.cls.php:92 +#: src/lang.cls.php:91 msgid "Default Public Cache TTL" msgstr "" -#: src/lang.cls.php:93 +#: src/lang.cls.php:92 msgid "Default Private Cache TTL" msgstr "" -#: src/lang.cls.php:94 +#: src/lang.cls.php:93 msgid "Default Front Page TTL" msgstr "" -#: src/lang.cls.php:95 +#: src/lang.cls.php:94 msgid "Default Feed TTL" msgstr "" -#: src/lang.cls.php:96 +#: src/lang.cls.php:95 msgid "Default REST TTL" msgstr "" -#: src/lang.cls.php:97 +#: src/lang.cls.php:96 msgid "Default HTTP Status Code Page TTL" msgstr "" -#: src/lang.cls.php:98 +#: src/lang.cls.php:97 msgid "Browser Cache TTL" msgstr "" -#: src/lang.cls.php:99 +#: src/lang.cls.php:98 msgid "AJAX Cache TTL" msgstr "" -#: src/lang.cls.php:100 +#: src/lang.cls.php:99 msgid "Automatically Upgrade" msgstr "" -#: src/lang.cls.php:101 +#: src/lang.cls.php:100 msgid "Guest Mode" msgstr "" -#: src/lang.cls.php:102 +#: src/lang.cls.php:101 msgid "Guest Optimization" msgstr "" -#: src/lang.cls.php:103 +#: src/lang.cls.php:102 msgid "Notifications" msgstr "" -#: src/lang.cls.php:104 +#: src/lang.cls.php:103 msgid "Cache Logged-in Users" msgstr "" -#: src/lang.cls.php:105 +#: src/lang.cls.php:104 msgid "Cache Commenters" msgstr "" -#: src/lang.cls.php:106 +#: src/lang.cls.php:105 msgid "Cache REST API" msgstr "" -#: src/lang.cls.php:107 +#: src/lang.cls.php:106 msgid "Cache Login Page" msgstr "" -#: src/lang.cls.php:108 +#: src/lang.cls.php:107 #: tpl/cache/settings_inc.cache_mobile.tpl.php:90 msgid "Cache Mobile" msgstr "" -#: src/lang.cls.php:109 +#: src/lang.cls.php:108 #: tpl/cache/settings_inc.cache_mobile.tpl.php:92 msgid "List of Mobile User Agents" msgstr "" -#: src/lang.cls.php:110 +#: src/lang.cls.php:109 msgid "Private Cached URIs" msgstr "" -#: src/lang.cls.php:111 +#: src/lang.cls.php:110 msgid "Drop Query String" msgstr "" -#: src/lang.cls.php:114 +#: src/lang.cls.php:113 msgid "Method" msgstr "" -#: src/lang.cls.php:115 +#: src/lang.cls.php:114 msgid "Host" msgstr "" -#: src/lang.cls.php:116 +#: src/lang.cls.php:115 msgid "Port" msgstr "" -#: src/lang.cls.php:117 +#: src/lang.cls.php:116 msgid "Default Object Lifetime" msgstr "" -#: src/lang.cls.php:118 +#: src/lang.cls.php:117 msgid "Username" msgstr "" -#: src/lang.cls.php:119 +#: src/lang.cls.php:118 msgid "Password" msgstr "" -#: src/lang.cls.php:120 +#: src/lang.cls.php:119 msgid "Redis Database ID" msgstr "" -#: src/lang.cls.php:121 +#: src/lang.cls.php:120 msgid "Global Groups" msgstr "" -#: src/lang.cls.php:122 +#: src/lang.cls.php:121 msgid "Do Not Cache Groups" msgstr "" -#: src/lang.cls.php:123 +#: src/lang.cls.php:122 msgid "Persistent Connection" msgstr "" -#: src/lang.cls.php:124 +#: src/lang.cls.php:123 msgid "Cache WP-Admin" msgstr "" -#: src/lang.cls.php:125 +#: src/lang.cls.php:124 msgid "Store Transients" msgstr "" -#: src/lang.cls.php:127 +#: src/lang.cls.php:126 msgid "Purge All On Upgrade" msgstr "" -#: src/lang.cls.php:128 +#: src/lang.cls.php:127 msgid "Serve Stale" msgstr "" -#: src/lang.cls.php:129 +#: src/lang.cls.php:128 #: tpl/cache/settings-purge.tpl.php:131 msgid "Scheduled Purge URLs" msgstr "" -#: src/lang.cls.php:130 +#: src/lang.cls.php:129 #: tpl/cache/settings-purge.tpl.php:106 msgid "Scheduled Purge Time" msgstr "" -#: src/lang.cls.php:131 +#: src/lang.cls.php:130 msgid "Force Cache URIs" msgstr "" -#: src/lang.cls.php:132 +#: src/lang.cls.php:131 msgid "Force Public Cache URIs" msgstr "" -#: src/lang.cls.php:133 +#: src/lang.cls.php:132 msgid "Do Not Cache URIs" msgstr "" -#: src/lang.cls.php:134 +#: src/lang.cls.php:133 msgid "Do Not Cache Query Strings" msgstr "" -#: src/lang.cls.php:135 +#: src/lang.cls.php:134 msgid "Do Not Cache Categories" msgstr "" -#: src/lang.cls.php:136 +#: src/lang.cls.php:135 msgid "Do Not Cache Tags" msgstr "" -#: src/lang.cls.php:137 +#: src/lang.cls.php:136 msgid "Do Not Cache Roles" msgstr "" -#: src/lang.cls.php:138 +#: src/lang.cls.php:137 msgid "CSS Minify" msgstr "" -#: src/lang.cls.php:139 +#: src/lang.cls.php:138 msgid "CSS Combine" msgstr "" -#: src/lang.cls.php:140 +#: src/lang.cls.php:139 msgid "CSS Combine External and Inline" msgstr "" -#: src/lang.cls.php:141 +#: src/lang.cls.php:140 msgid "Generate UCSS" msgstr "" -#: src/lang.cls.php:142 +#: src/lang.cls.php:141 msgid "UCSS Inline" msgstr "" -#: src/lang.cls.php:143 +#: src/lang.cls.php:142 msgid "UCSS Selector Allowlist" msgstr "" +#: src/lang.cls.php:143 +msgid "UCSS Inline Excluded Files" +msgstr "" + #: src/lang.cls.php:144 -msgid "UCSS File Excludes and Inline" +msgid "UCSS URI Excludes" msgstr "" #: src/lang.cls.php:145 -msgid "UCSS URI Excludes" +msgid "JS Minify" msgstr "" #: src/lang.cls.php:146 -msgid "JS Minify" +msgid "JS Combine" msgstr "" -#: src/lang.cls.php:148 +#: src/lang.cls.php:147 msgid "JS Combine External and Inline" msgstr "" -#: src/lang.cls.php:149 +#: src/lang.cls.php:148 msgid "HTML Minify" msgstr "" -#: src/lang.cls.php:150 +#: src/lang.cls.php:149 msgid "HTML Lazy Load Selectors" msgstr "" -#: src/lang.cls.php:151 +#: src/lang.cls.php:150 msgid "HTML Keep Comments" msgstr "" -#: src/lang.cls.php:152 -#: tpl/page_optm/settings_tuning_css.tpl.php:158 +#: src/lang.cls.php:151 +#: tpl/page_optm/settings_tuning_css.tpl.php:167 msgid "Load CSS Asynchronously" msgstr "" -#: src/lang.cls.php:153 +#: src/lang.cls.php:152 msgid "CCSS Per URL" msgstr "" -#: src/lang.cls.php:154 +#: src/lang.cls.php:153 msgid "Inline CSS Async Lib" msgstr "" -#: src/lang.cls.php:155 -#: tpl/presets/standard.tpl.php:40 +#: src/lang.cls.php:154 +#: tpl/presets/standard.tpl.php:46 msgid "Font Display Optimization" msgstr "" -#: src/lang.cls.php:156 +#: src/lang.cls.php:155 msgid "Load JS Deferred" msgstr "" -#: src/lang.cls.php:157 +#: src/lang.cls.php:156 msgid "Localize Resources" msgstr "" -#: src/lang.cls.php:158 +#: src/lang.cls.php:157 msgid "Localization Files" msgstr "" -#: src/lang.cls.php:159 +#: src/lang.cls.php:158 msgid "DNS Prefetch" msgstr "" -#: src/lang.cls.php:160 +#: src/lang.cls.php:159 msgid "DNS Prefetch Control" msgstr "" -#: src/lang.cls.php:161 +#: src/lang.cls.php:160 msgid "DNS Preconnect" msgstr "" -#: src/lang.cls.php:162 +#: src/lang.cls.php:161 msgid "CSS Excludes" msgstr "" -#: src/lang.cls.php:163 +#: src/lang.cls.php:162 msgid "JS Delayed Includes" msgstr "" -#: src/lang.cls.php:164 +#: src/lang.cls.php:163 msgid "JS Excludes" msgstr "" -#: src/lang.cls.php:165 +#: src/lang.cls.php:164 msgid "Remove Query Strings" msgstr "" -#: src/lang.cls.php:166 +#: src/lang.cls.php:165 msgid "Load Google Fonts Asynchronously" msgstr "" -#: src/lang.cls.php:167 +#: src/lang.cls.php:166 msgid "Remove Google Fonts" msgstr "" -#: src/lang.cls.php:168 +#: src/lang.cls.php:167 msgid "Critical CSS Rules" msgstr "" -#: src/lang.cls.php:169 +#: src/lang.cls.php:168 msgid "Separate CCSS Cache Post Types" msgstr "" -#: src/lang.cls.php:170 +#: src/lang.cls.php:169 msgid "Separate CCSS Cache URIs" msgstr "" -#: src/lang.cls.php:171 +#: src/lang.cls.php:170 msgid "CCSS Selector Allowlist" msgstr "" -#: src/lang.cls.php:172 +#: src/lang.cls.php:171 msgid "JS Deferred / Delayed Excludes" msgstr "" -#: src/lang.cls.php:173 +#: src/lang.cls.php:172 msgid "Guest Mode JS Excludes" msgstr "" -#: src/lang.cls.php:174 -#: tpl/presets/standard.tpl.php:45 +#: src/lang.cls.php:173 +#: tpl/presets/standard.tpl.php:51 msgid "Remove WordPress Emoji" msgstr "" -#: src/lang.cls.php:175 -#: tpl/presets/standard.tpl.php:46 +#: src/lang.cls.php:174 +#: tpl/presets/standard.tpl.php:52 msgid "Remove Noscript Tags" msgstr "" -#: src/lang.cls.php:176 +#: src/lang.cls.php:175 msgid "URI Excludes" msgstr "" -#: src/lang.cls.php:177 +#: src/lang.cls.php:176 msgid "Optimize for Guests Only" msgstr "" -#: src/lang.cls.php:178 +#: src/lang.cls.php:177 msgid "Role Excludes" msgstr "" -#: src/lang.cls.php:181 +#: src/lang.cls.php:180 msgid "Gravatar Cache Cron" msgstr "" -#: src/lang.cls.php:182 +#: src/lang.cls.php:181 msgid "Gravatar Cache TTL" msgstr "" -#: src/lang.cls.php:184 +#: src/lang.cls.php:183 msgid "Lazy Load Images" msgstr "" -#: src/lang.cls.php:185 +#: src/lang.cls.php:184 msgid "Lazy Load Image Excludes" msgstr "" -#: src/lang.cls.php:186 +#: src/lang.cls.php:185 msgid "Lazy Load Image Class Name Excludes" msgstr "" -#: src/lang.cls.php:187 +#: src/lang.cls.php:186 msgid "Lazy Load Image Parent Class Name Excludes" msgstr "" -#: src/lang.cls.php:188 +#: src/lang.cls.php:187 msgid "Lazy Load Iframe Class Name Excludes" msgstr "" -#: src/lang.cls.php:189 +#: src/lang.cls.php:188 msgid "Lazy Load Iframe Parent Class Name Excludes" msgstr "" -#: src/lang.cls.php:190 +#: src/lang.cls.php:189 msgid "Lazy Load URI Excludes" msgstr "" -#: src/lang.cls.php:191 +#: src/lang.cls.php:190 msgid "LQIP Excludes" msgstr "" -#: src/lang.cls.php:192 +#: src/lang.cls.php:191 msgid "Basic Image Placeholder" msgstr "" -#: src/lang.cls.php:193 +#: src/lang.cls.php:192 msgid "Responsive Placeholder" msgstr "" -#: src/lang.cls.php:194 +#: src/lang.cls.php:193 msgid "Responsive Placeholder Color" msgstr "" -#: src/lang.cls.php:195 +#: src/lang.cls.php:194 msgid "Responsive Placeholder SVG" msgstr "" -#: src/lang.cls.php:196 +#: src/lang.cls.php:195 msgid "LQIP Cloud Generator" msgstr "" -#: src/lang.cls.php:197 +#: src/lang.cls.php:196 msgid "LQIP Quality" msgstr "" -#: src/lang.cls.php:198 +#: src/lang.cls.php:197 msgid "LQIP Minimum Dimensions" msgstr "" -#: src/lang.cls.php:200 +#: src/lang.cls.php:199 msgid "Generate LQIP In Background" msgstr "" -#: src/lang.cls.php:201 +#: src/lang.cls.php:200 msgid "Lazy Load Iframes" msgstr "" -#: src/lang.cls.php:202 +#: src/lang.cls.php:201 msgid "Add Missing Sizes" msgstr "" -#: src/lang.cls.php:203 +#: src/lang.cls.php:202 +#: src/metabox.cls.php:32 #: src/metabox.cls.php:33 -#: src/metabox.cls.php:34 -#: tpl/page_optm/settings_vpi.tpl.php:15 +#: tpl/page_optm/settings_vpi.tpl.php:23 msgid "Viewport Images" msgstr "" -#: src/lang.cls.php:204 +#: src/lang.cls.php:203 msgid "Viewport Images Cron" msgstr "" +#: src/lang.cls.php:204 +msgid "Auto Rescale Original Images" +msgstr "" + #: src/lang.cls.php:206 msgid "Auto Request Cron" msgstr "" @@ -1670,428 +1687,435 @@ msgid "Optimize Losslessly" msgstr "" #: src/lang.cls.php:211 -msgid "Preserve EXIF/XMP data" +msgid "Optimize Image Sizes" msgstr "" #: src/lang.cls.php:212 -msgid "WebP/AVIF Attribute To Replace" +msgid "Preserve EXIF/XMP data" msgstr "" #: src/lang.cls.php:213 -msgid "WebP/AVIF For Extra srcset" +msgid "WebP/AVIF Attribute To Replace" msgstr "" #: src/lang.cls.php:214 -msgid "WordPress Image Quality Control" +msgid "WebP/AVIF For Extra srcset" msgstr "" #: src/lang.cls.php:215 -#: tpl/esi_widget_edit.php:36 -msgid "Enable ESI" +msgid "WordPress Image Quality Control" msgstr "" #: src/lang.cls.php:216 -msgid "Cache Admin Bar" +#: tpl/esi_widget_edit.php:43 +msgid "Enable ESI" msgstr "" #: src/lang.cls.php:217 -msgid "Cache Comment Form" +msgid "Cache Admin Bar" msgstr "" #: src/lang.cls.php:218 -msgid "ESI Nonces" +msgid "Cache Comment Form" msgstr "" #: src/lang.cls.php:219 -#: tpl/page_optm/settings_css.tpl.php:139 -#: tpl/page_optm/settings_css.tpl.php:283 -#: tpl/page_optm/settings_vpi.tpl.php:86 -msgid "Vary Group" +msgid "ESI Nonces" msgstr "" #: src/lang.cls.php:220 -msgid "Purge All Hooks" +#: tpl/page_optm/settings_css.tpl.php:140 +#: tpl/page_optm/settings_css.tpl.php:277 +#: tpl/page_optm/settings_vpi.tpl.php:88 +msgid "Vary Group" msgstr "" #: src/lang.cls.php:221 +msgid "Purge All Hooks" +msgstr "" + +#: src/lang.cls.php:222 msgid "Improve HTTP/HTTPS Compatibility" msgstr "" -#: src/lang.cls.php:222 +#: src/lang.cls.php:223 msgid "Instant Click" msgstr "" -#: src/lang.cls.php:223 +#: src/lang.cls.php:224 msgid "Do Not Cache Cookies" msgstr "" -#: src/lang.cls.php:224 +#: src/lang.cls.php:225 msgid "Do Not Cache User Agents" msgstr "" -#: src/lang.cls.php:225 +#: src/lang.cls.php:226 msgid "Login Cookie" msgstr "" -#: src/lang.cls.php:226 +#: src/lang.cls.php:227 msgid "Vary Cookies" msgstr "" -#: src/lang.cls.php:228 +#: src/lang.cls.php:229 msgid "Frontend Heartbeat Control" msgstr "" -#: src/lang.cls.php:229 +#: src/lang.cls.php:230 msgid "Frontend Heartbeat TTL" msgstr "" -#: src/lang.cls.php:230 +#: src/lang.cls.php:231 msgid "Backend Heartbeat Control" msgstr "" -#: src/lang.cls.php:231 +#: src/lang.cls.php:232 msgid "Backend Heartbeat TTL" msgstr "" -#: src/lang.cls.php:232 +#: src/lang.cls.php:233 msgid "Editor Heartbeat" msgstr "" -#: src/lang.cls.php:233 +#: src/lang.cls.php:234 msgid "Editor Heartbeat TTL" msgstr "" -#: src/lang.cls.php:235 +#: src/lang.cls.php:236 msgid "Use CDN Mapping" msgstr "" -#: src/lang.cls.php:236 +#: src/lang.cls.php:237 msgid "CDN URL" msgstr "" -#: src/lang.cls.php:237 +#: src/lang.cls.php:238 msgid "Include Images" msgstr "" -#: src/lang.cls.php:238 +#: src/lang.cls.php:239 msgid "Include CSS" msgstr "" -#: src/lang.cls.php:239 +#: src/lang.cls.php:240 msgid "Include JS" msgstr "" -#: src/lang.cls.php:240 -#: tpl/cdn/other.tpl.php:87 +#: src/lang.cls.php:241 +#: tpl/cdn/other.tpl.php:113 msgid "Include File Types" msgstr "" -#: src/lang.cls.php:241 +#: src/lang.cls.php:242 msgid "HTML Attribute To Replace" msgstr "" -#: src/lang.cls.php:242 +#: src/lang.cls.php:243 msgid "Original URLs" msgstr "" -#: src/lang.cls.php:243 +#: src/lang.cls.php:244 msgid "Included Directories" msgstr "" -#: src/lang.cls.php:244 +#: src/lang.cls.php:245 msgid "Exclude Path" msgstr "" -#: src/lang.cls.php:245 +#: src/lang.cls.php:246 msgid "Cloudflare API" msgstr "" -#: src/lang.cls.php:246 +#: src/lang.cls.php:247 msgid "Clear Cloudflare cache" msgstr "" -#: src/lang.cls.php:249 +#: src/lang.cls.php:250 msgid "Crawl Interval" msgstr "" -#: src/lang.cls.php:250 +#: src/lang.cls.php:251 msgid "Server Load Limit" msgstr "" -#: src/lang.cls.php:251 +#: src/lang.cls.php:252 msgid "Role Simulation" msgstr "" -#: src/lang.cls.php:252 +#: src/lang.cls.php:253 msgid "Cookie Simulation" msgstr "" -#: src/lang.cls.php:253 +#: src/lang.cls.php:254 msgid "Custom Sitemap" msgstr "" -#: src/lang.cls.php:255 -#: tpl/inc/disabled_all.php:6 +#: src/lang.cls.php:256 msgid "Disable All Features" msgstr "" -#: src/lang.cls.php:256 -#: tpl/toolbox/log_viewer.tpl.php:11 +#: src/lang.cls.php:257 +#: tpl/toolbox/log_viewer.tpl.php:18 msgid "Debug Log" msgstr "" -#: src/lang.cls.php:257 +#: src/lang.cls.php:258 msgid "Admin IPs" msgstr "" -#: src/lang.cls.php:258 +#: src/lang.cls.php:259 msgid "Debug Level" msgstr "" -#: src/lang.cls.php:259 +#: src/lang.cls.php:260 msgid "Log File Size Limit" msgstr "" -#: src/lang.cls.php:260 +#: src/lang.cls.php:261 msgid "Collapse Query Strings" msgstr "" -#: src/lang.cls.php:261 +#: src/lang.cls.php:262 msgid "Debug URI Includes" msgstr "" -#: src/lang.cls.php:262 +#: src/lang.cls.php:263 msgid "Debug URI Excludes" msgstr "" -#: src/lang.cls.php:263 +#: src/lang.cls.php:264 msgid "Debug String Excludes" msgstr "" -#: src/lang.cls.php:265 +#: src/lang.cls.php:266 msgid "Revisions Max Number" msgstr "" -#: src/lang.cls.php:266 +#: src/lang.cls.php:267 msgid "Revisions Max Age" msgstr "" -#: src/media.cls.php:258 +#: src/media.cls.php:304 msgid "LiteSpeed Optimization" msgstr "" -#: src/media.cls.php:307 -#: src/media.cls.php:330 -#: src/media.cls.php:356 -#: src/media.cls.php:389 +#: src/media.cls.php:353 +#: src/media.cls.php:376 +#: src/media.cls.php:402 +#: src/media.cls.php:435 msgid "(optm)" msgstr "" -#: src/media.cls.php:308 +#: src/media.cls.php:354 msgid "Currently using optimized version of file." msgstr "" -#: src/media.cls.php:308 -#: src/media.cls.php:360 +#: src/media.cls.php:354 +#: src/media.cls.php:406 msgid "Click to switch to original (unoptimized) version." msgstr "" -#: src/media.cls.php:311 -#: src/media.cls.php:363 +#: src/media.cls.php:357 +#: src/media.cls.php:409 msgid "(non-optm)" msgstr "" -#: src/media.cls.php:312 +#: src/media.cls.php:358 msgid "Currently using original (unoptimized) version of file." msgstr "" -#: src/media.cls.php:312 -#: src/media.cls.php:367 +#: src/media.cls.php:358 +#: src/media.cls.php:413 msgid "Click to switch to optimized version." msgstr "" -#: src/media.cls.php:318 +#: src/media.cls.php:364 msgid "Original file reduced by %1$s (%2$s)" msgstr "" -#: src/media.cls.php:322 +#: src/media.cls.php:368 msgid "Orig saved %s" msgstr "" -#: src/media.cls.php:329 -#: src/media.cls.php:387 +#: src/media.cls.php:375 +#: src/media.cls.php:433 msgid "Using optimized version of file. " msgstr "" -#: src/media.cls.php:329 +#: src/media.cls.php:375 msgid "No backup of original file exists." msgstr "" -#: src/media.cls.php:334 +#: src/media.cls.php:380 msgid "Congratulation! Your file was already optimized" msgstr "" -#: src/media.cls.php:335 +#: src/media.cls.php:381 msgid "Orig %s" msgstr "" -#: src/media.cls.php:335 +#: src/media.cls.php:381 msgid "(no savings)" msgstr "" -#: src/media.cls.php:337 +#: src/media.cls.php:383 msgid "Orig" msgstr "" -#: src/media.cls.php:358 +#: src/media.cls.php:404 msgid "Currently using optimized version of AVIF file." msgstr "" -#: src/media.cls.php:359 +#: src/media.cls.php:405 msgid "Currently using optimized version of WebP file." msgstr "" -#: src/media.cls.php:365 +#: src/media.cls.php:411 msgid "Currently using original (unoptimized) version of AVIF file." msgstr "" -#: src/media.cls.php:366 +#: src/media.cls.php:412 msgid "Currently using original (unoptimized) version of WebP file." msgstr "" -#: src/media.cls.php:374 +#: src/media.cls.php:420 msgid "AVIF file reduced by %1$s (%2$s)" msgstr "" -#: src/media.cls.php:374 +#: src/media.cls.php:420 msgid "WebP file reduced by %1$s (%2$s)" msgstr "" -#: src/media.cls.php:380 +#: src/media.cls.php:426 msgid "AVIF saved %s" msgstr "" -#: src/media.cls.php:380 +#: src/media.cls.php:426 msgid "WebP saved %s" msgstr "" -#: src/media.cls.php:388 +#: src/media.cls.php:434 msgid "No backup of unoptimized AVIF file exists." msgstr "" -#: src/media.cls.php:388 +#: src/media.cls.php:434 msgid "No backup of unoptimized WebP file exists." msgstr "" -#: src/media.cls.php:403 +#: src/media.cls.php:449 msgid "Restore from backup" msgstr "" -#: src/metabox.cls.php:30 +#: src/metabox.cls.php:29 msgid "Disable Cache" msgstr "" -#: src/metabox.cls.php:31 +#: src/metabox.cls.php:30 msgid "Disable Image Lazyload" msgstr "" -#: src/metabox.cls.php:32 +#: src/metabox.cls.php:31 msgid "Disable VPI" msgstr "" -#: src/metabox.cls.php:34 +#: src/metabox.cls.php:33 msgid "Mobile" msgstr "" -#: src/metabox.cls.php:63 -msgid "LiteSpeed Options" -msgstr "" - -#: src/object-cache.cls.php:484 +#: src/object-cache.cls.php:714 msgid "Redis encountered a fatal error: %1$s (code: %2$d)" msgstr "" -#: src/placeholder.cls.php:84 +#: src/placeholder.cls.php:83 msgid "LQIP" msgstr "" -#: src/placeholder.cls.php:141 +#: src/placeholder.cls.php:140 msgid "LQIP image preview for size %s" msgstr "" -#: src/purge.cls.php:212 +#: src/purge.cls.php:213 msgid "Purged all caches successfully." msgstr "" -#: src/purge.cls.php:234 +#: src/purge.cls.php:235 msgid "Notified LiteSpeed Web Server to purge all LSCache entries." msgstr "" -#: src/purge.cls.php:253 +#: src/purge.cls.php:254 msgid "Cleaned all Critical CSS files." msgstr "" -#: src/purge.cls.php:272 +#: src/purge.cls.php:273 msgid "Cleaned all Unique CSS files." msgstr "" -#: src/purge.cls.php:310 +#: src/purge.cls.php:311 msgid "Cleaned all LQIP files." msgstr "" -#: src/purge.cls.php:327 +#: src/purge.cls.php:328 msgid "Cleaned all Gravatar files." msgstr "" -#: src/purge.cls.php:344 +#: src/purge.cls.php:345 msgid "Cleaned all localized resource entries." msgstr "" -#: src/purge.cls.php:378 +#: src/purge.cls.php:379 msgid "Notified LiteSpeed Web Server to purge CSS/JS entries." msgstr "" -#: src/purge.cls.php:394 -msgid "Opcode cache is not enabled." +#: src/purge.cls.php:396 +msgid "OPcache is not enabled." +msgstr "" + +#: src/purge.cls.php:407 +msgid "OPcache is restricted by %s setting." +msgstr "" + +#: src/purge.cls.php:419 +msgid "Reset the OPcache failed." msgstr "" -#: src/purge.cls.php:409 -msgid "Reset the entire opcode cache successfully." +#: src/purge.cls.php:432 +msgid "Reset the entire OPcache successfully." msgstr "" -#: src/purge.cls.php:437 +#: src/purge.cls.php:460 msgid "Object cache is not enabled." msgstr "" -#: src/purge.cls.php:450 +#: src/purge.cls.php:473 msgid "Purge all object caches successfully." msgstr "" -#: src/purge.cls.php:661 +#: src/purge.cls.php:684 msgid "Notified LiteSpeed Web Server to purge the front page." msgstr "" -#: src/purge.cls.php:675 +#: src/purge.cls.php:698 msgid "Notified LiteSpeed Web Server to purge all pages." msgstr "" -#: src/purge.cls.php:695 +#: src/purge.cls.php:718 msgid "Notified LiteSpeed Web Server to purge error pages." msgstr "" -#: src/purge.cls.php:722 +#: src/purge.cls.php:745 msgid "Purge category %s" msgstr "" -#: src/purge.cls.php:751 +#: src/purge.cls.php:774 msgid "Purge tag %s" msgstr "" -#: src/purge.cls.php:785 +#: src/purge.cls.php:808 msgid "Purge url %s" msgstr "" -#: src/root.cls.php:197 +#: src/root.cls.php:198 msgid "All QUIC.cloud service queues have been cleared." msgstr "" @@ -2103,8 +2127,8 @@ msgstr "" msgid "LiteSpeed Crawler Cron" msgstr "" -#: src/tool.cls.php:36 -#: src/tool.cls.php:47 +#: src/tool.cls.php:44 +#: src/tool.cls.php:55 msgid "Failed to detect IP" msgstr "" @@ -2117,88 +2141,83 @@ msgid "just now" msgstr "" #: src/utility.cls.php:231 -#: tpl/dash/dashboard.tpl.php:434 -#: tpl/dash/dashboard.tpl.php:503 msgid " %s ago" msgstr "" -#: thirdparty/litespeed-check.cls.php:74 -#: thirdparty/litespeed-check.cls.php:122 +#: thirdparty/litespeed-check.cls.php:75 +#: thirdparty/litespeed-check.cls.php:123 msgid "Please consider disabling the following detected plugins, as they may conflict with LiteSpeed Cache:" msgstr "" -#: thirdparty/woocommerce.content.tpl.php:17 +#: thirdparty/woocommerce.content.tpl.php:18 msgid "WooCommerce Settings" msgstr "" -#: thirdparty/woocommerce.content.tpl.php:22 +#: thirdparty/woocommerce.content.tpl.php:23 #: tpl/cache/settings-advanced.tpl.php:21 #: tpl/cache/settings_inc.browser.tpl.php:23 -#: tpl/toolbox/heartbeat.tpl.php:15 -#: tpl/toolbox/report.tpl.php:38 +#: tpl/toolbox/beta_test.tpl.php:40 +#: tpl/toolbox/heartbeat.tpl.php:24 +#: tpl/toolbox/report.tpl.php:46 msgid "NOTICE:" msgstr "" -#: thirdparty/woocommerce.content.tpl.php:23 +#: thirdparty/woocommerce.content.tpl.php:24 msgid "After verifying that the cache works in general, please test the cart." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:24 +#: thirdparty/woocommerce.content.tpl.php:25 msgid "To test the cart, visit the FAQ." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:25 +#: thirdparty/woocommerce.content.tpl.php:26 msgid "By default, the My Account, Checkout, and Cart pages are automatically excluded from caching. Misconfiguration of page associations in WooCommerce settings may cause some pages to be erroneously excluded." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:33 +#: thirdparty/woocommerce.content.tpl.php:34 msgid "Product Update Interval" msgstr "" -#: thirdparty/woocommerce.content.tpl.php:38 +#: thirdparty/woocommerce.content.tpl.php:39 msgid "Purge product on changes to the quantity or stock status." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:38 +#: thirdparty/woocommerce.content.tpl.php:39 msgid "Purge categories only when stock status changes." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:39 +#: thirdparty/woocommerce.content.tpl.php:40 msgid "Purge product and categories only when the stock status changes." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:40 +#: thirdparty/woocommerce.content.tpl.php:41 msgid "Purge product only when the stock status changes." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:40 +#: thirdparty/woocommerce.content.tpl.php:41 msgid "Do not purge categories on changes to the quantity or stock status." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:41 +#: thirdparty/woocommerce.content.tpl.php:42 msgid "Always purge both product and categories on changes to the quantity or stock status." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:54 +#: thirdparty/woocommerce.content.tpl.php:55 msgid "Determines how changes in product quantity and product stock status affect product pages and their associated category pages." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:62 +#: thirdparty/woocommerce.content.tpl.php:63 msgid "Vary for Mini Cart" msgstr "" -#: thirdparty/woocommerce.content.tpl.php:70 +#: thirdparty/woocommerce.content.tpl.php:71 msgid "Generate a separate vary cache copy for the mini cart when the cart is not empty." msgstr "" -#: thirdparty/woocommerce.content.tpl.php:71 +#: thirdparty/woocommerce.content.tpl.php:72 msgid "If your theme does not use JS to update the mini cart, you must enable this option to display the correct cart contents." msgstr "" -#: thirdparty/woocommerce.tab.tpl.php:3 -msgid "WooCommerce" -msgstr "" - #: tpl/banner/cloud_news.tpl.php:30 #: tpl/banner/cloud_news.tpl.php:41 msgid "Install" @@ -2222,8 +2241,8 @@ msgid "Send to twitter to get %s bonus" msgstr "" #: tpl/banner/cloud_promo.tpl.php:40 -#: tpl/page_optm/settings_tuning_css.tpl.php:59 -#: tpl/page_optm/settings_tuning_css.tpl.php:135 +#: tpl/page_optm/settings_tuning_css.tpl.php:69 +#: tpl/page_optm/settings_tuning_css.tpl.php:144 msgid "Learn more" msgstr "" @@ -2245,7 +2264,7 @@ msgstr "" #: tpl/banner/new_version.php:77 #: tpl/banner/new_version_dev.tpl.php:41 -#: tpl/toolbox/beta_test.tpl.php:60 +#: tpl/toolbox/beta_test.tpl.php:86 msgid "Upgrade" msgstr "" @@ -2258,7 +2277,7 @@ msgid "Maybe Later" msgstr "" #: tpl/banner/new_version.php:113 -#: tpl/banner/score.php:139 +#: tpl/banner/score.php:141 #: tpl/banner/slack.php:48 msgid "Dismiss this notice." msgstr "" @@ -2271,62 +2290,62 @@ msgstr "" msgid "New developer version %s is available now." msgstr "" -#: tpl/banner/score.php:34 +#: tpl/banner/score.php:36 msgid "Thank You for Using the LiteSpeed Cache Plugin!" msgstr "" -#: tpl/banner/score.php:38 -#: tpl/dash/dashboard.tpl.php:373 +#: tpl/banner/score.php:40 +#: tpl/dash/dashboard.tpl.php:374 msgid "Page Load Time" msgstr "" -#: tpl/banner/score.php:43 -#: tpl/banner/score.php:77 -#: tpl/dash/dashboard.tpl.php:389 +#: tpl/banner/score.php:45 +#: tpl/banner/score.php:79 +#: tpl/dash/dashboard.tpl.php:394 #: tpl/dash/dashboard.tpl.php:470 msgid "Before" msgstr "" -#: tpl/banner/score.php:51 -#: tpl/banner/score.php:85 -#: tpl/dash/dashboard.tpl.php:398 +#: tpl/banner/score.php:53 +#: tpl/banner/score.php:87 +#: tpl/dash/dashboard.tpl.php:402 #: tpl/dash/dashboard.tpl.php:478 msgid "After" msgstr "" -#: tpl/banner/score.php:60 -#: tpl/banner/score.php:94 -#: tpl/dash/dashboard.tpl.php:406 +#: tpl/banner/score.php:62 +#: tpl/banner/score.php:96 +#: tpl/dash/dashboard.tpl.php:410 #: tpl/dash/dashboard.tpl.php:486 msgid "Improved by" msgstr "" -#: tpl/banner/score.php:72 -#: tpl/dash/dashboard.tpl.php:450 +#: tpl/banner/score.php:74 +#: tpl/dash/dashboard.tpl.php:455 msgid "PageSpeed Score" msgstr "" -#: tpl/banner/score.php:110 +#: tpl/banner/score.php:112 msgid "Sure I'd love to review!" msgstr "" -#: tpl/banner/score.php:114 +#: tpl/banner/score.php:116 msgid "I've already left a review" msgstr "" -#: tpl/banner/score.php:115 +#: tpl/banner/score.php:117 msgid "Maybe later" msgstr "" -#: tpl/banner/score.php:119 +#: tpl/banner/score.php:121 msgid "Created with ❤️ by LiteSpeed team." msgstr "" -#: tpl/banner/score.php:120 +#: tpl/banner/score.php:122 msgid "Support forum" msgstr "" -#: tpl/banner/score.php:120 +#: tpl/banner/score.php:122 msgid "Submit a ticket" msgstr "" @@ -2354,8 +2373,8 @@ msgstr "" #: tpl/cache/entry.tpl.php:18 #: tpl/cache/entry_network.tpl.php:17 -#: tpl/toolbox/entry.tpl.php:7 -#: tpl/toolbox/purge.tpl.php:134 +#: tpl/toolbox/entry.tpl.php:16 +#: tpl/toolbox/purge.tpl.php:141 msgid "Purge" msgstr "" @@ -2380,7 +2399,7 @@ msgstr "" #: tpl/cache/entry.tpl.php:28 #: tpl/cache/entry_network.tpl.php:21 -#: tpl/toolbox/settings-debug.tpl.php:86 +#: tpl/toolbox/settings-debug.tpl.php:117 msgid "Advanced" msgstr "" @@ -2395,9 +2414,9 @@ msgstr "" #: tpl/cache/more_settings_tip.tpl.php:22 #: tpl/cache/settings-excludes.tpl.php:71 #: tpl/cache/settings-excludes.tpl.php:104 -#: tpl/cdn/other.tpl.php:63 -#: tpl/crawler/settings.tpl.php:73 -#: tpl/crawler/settings.tpl.php:78 +#: tpl/cdn/other.tpl.php:79 +#: tpl/crawler/settings.tpl.php:76 +#: tpl/crawler/settings.tpl.php:86 msgid "NOTE" msgstr "" @@ -2466,16 +2485,17 @@ msgstr "" msgid "Use Network Admin Setting" msgstr "" -#. translators: %1$s: Opening link tag, %2$s: Closing link tag +#. translators: %s: Link tags #: tpl/cache/settings-cache.tpl.php:36 -msgid "Please visit the %1$sInformation%2$s page on how to test the cache." +msgid "Please visit the %sInformation%s page on how to test the cache." msgstr "" #: tpl/cache/settings-cache.tpl.php:42 -#: tpl/crawler/settings.tpl.php:104 #: tpl/crawler/settings.tpl.php:113 -#: tpl/crawler/summary.tpl.php:183 -#: tpl/page_optm/entry.tpl.php:33 +#: tpl/crawler/settings.tpl.php:133 +#: tpl/crawler/summary.tpl.php:208 +#: tpl/page_optm/entry.tpl.php:42 +#: tpl/toolbox/settings-debug.tpl.php:47 msgid "NOTICE" msgstr "" @@ -2558,13 +2578,13 @@ msgid "ESI sample for developers" msgstr "" #: tpl/cache/settings-esi.tpl.php:45 -#: tpl/cdn/cf.tpl.php:95 -#: tpl/crawler/summary.tpl.php:53 -#: tpl/inc/check_cache_disabled.php:31 -#: tpl/inc/check_if_network_disable_all.php:19 -#: tpl/page_optm/settings_css.tpl.php:72 -#: tpl/page_optm/settings_css.tpl.php:212 -#: tpl/page_optm/settings_localization.tpl.php:12 +#: tpl/cdn/cf.tpl.php:100 +#: tpl/crawler/summary.tpl.php:60 +#: tpl/inc/check_cache_disabled.php:38 +#: tpl/inc/check_if_network_disable_all.php:28 +#: tpl/page_optm/settings_css.tpl.php:77 +#: tpl/page_optm/settings_css.tpl.php:211 +#: tpl/page_optm/settings_localization.tpl.php:21 msgid "WARNING" msgstr "" @@ -2584,41 +2604,41 @@ msgstr "" msgid "Cache the built-in Comment Form ESI block." msgstr "" -#: tpl/cache/settings-esi.tpl.php:102 +#: tpl/cache/settings-esi.tpl.php:100 msgid "The list will be merged with the predefined nonces in your local data file." msgstr "" -#: tpl/cache/settings-esi.tpl.php:103 +#: tpl/cache/settings-esi.tpl.php:101 msgid "The latest data file is" msgstr "" -#: tpl/cache/settings-esi.tpl.php:106 -#: tpl/page_optm/settings_media_exc.tpl.php:28 -#: tpl/page_optm/settings_tuning.tpl.php:40 -#: tpl/page_optm/settings_tuning.tpl.php:60 -#: tpl/page_optm/settings_tuning.tpl.php:81 -#: tpl/page_optm/settings_tuning.tpl.php:102 -#: tpl/page_optm/settings_tuning.tpl.php:121 -#: tpl/page_optm/settings_tuning_css.tpl.php:26 -#: tpl/page_optm/settings_tuning_css.tpl.php:87 +#: tpl/cache/settings-esi.tpl.php:104 +#: tpl/page_optm/settings_media_exc.tpl.php:37 +#: tpl/page_optm/settings_tuning.tpl.php:49 +#: tpl/page_optm/settings_tuning.tpl.php:69 +#: tpl/page_optm/settings_tuning.tpl.php:90 +#: tpl/page_optm/settings_tuning.tpl.php:111 +#: tpl/page_optm/settings_tuning.tpl.php:130 +#: tpl/page_optm/settings_tuning_css.tpl.php:36 +#: tpl/page_optm/settings_tuning_css.tpl.php:97 msgid "Filter %s is supported." msgstr "" -#: tpl/cache/settings-esi.tpl.php:112 +#: tpl/cache/settings-esi.tpl.php:108 msgid "The above nonces will be converted to ESI automatically." msgstr "" -#: tpl/cache/settings-esi.tpl.php:114 +#: tpl/cache/settings-esi.tpl.php:110 msgid "An optional second parameter may be used to specify cache control. Use a space to separate" msgstr "" -#: tpl/cache/settings-esi.tpl.php:117 +#: tpl/cache/settings-esi.tpl.php:113 #: tpl/cache/settings-purge.tpl.php:111 -#: tpl/cdn/other.tpl.php:128 +#: tpl/cdn/other.tpl.php:169 msgid "Wildcard %1$s supported (match zero or more characters). For example, to match %2$s and %3$s, use %4$s." msgstr "" -#: tpl/cache/settings-esi.tpl.php:145 +#: tpl/cache/settings-esi.tpl.php:141 msgid "If your site contains public content that certain user roles can see but other roles cannot, you can specify a Vary Group for those user roles. For example, specifying an administrator vary group allows there to be a separate publicly-cached page tailored to administrators (with “edit” links, etc), while all other user roles see the default public page." msgstr "" @@ -2627,11 +2647,8 @@ msgid "Paths containing these strings will not be cached." msgstr "" #: tpl/cache/settings-excludes.tpl.php:32 -#: tpl/page_optm/settings_tuning.tpl.php:62 -#: tpl/page_optm/settings_tuning.tpl.php:83 -#: tpl/page_optm/settings_tuning_css.tpl.php:28 -#: tpl/page_optm/settings_tuning_css.tpl.php:68 -#: tpl/page_optm/settings_tuning_css.tpl.php:144 +#: tpl/page_optm/settings_tuning_css.tpl.php:78 +#: tpl/page_optm/settings_tuning_css.tpl.php:153 msgid "Predefined list will also be combined w/ the above settings" msgstr "" @@ -2735,8 +2752,8 @@ msgstr "" #: tpl/cache/settings-purge.tpl.php:53 #: tpl/cache/settings-purge.tpl.php:90 #: tpl/cache/settings-purge.tpl.php:114 -#: tpl/page_optm/settings_tuning_css.tpl.php:62 -#: tpl/page_optm/settings_tuning_css.tpl.php:138 +#: tpl/page_optm/settings_tuning_css.tpl.php:72 +#: tpl/page_optm/settings_tuning_css.tpl.php:147 msgid "Note" msgstr "" @@ -2833,9 +2850,9 @@ msgstr "" msgid "Browser caching stores static files locally in the user's browser. Turn on this setting to reduce repeated requests for static files." msgstr "" -#. translators: %1$s: Opening link tag, %2$s: Closing link tag +#. translators: %s: Link tags #: tpl/cache/settings_inc.browser.tpl.php:46 -msgid "You can turn on browser caching in server admin too. %1$sLearn more about LiteSpeed browser cache settings%2$s." +msgid "You can turn on browser caching in server admin too. %sLearn more about LiteSpeed browser cache settings%s." msgstr "" #: tpl/cache/settings_inc.browser.tpl.php:63 @@ -2942,12 +2959,10 @@ msgid "You can list the 3rd party vary cookies here." msgstr "" #: tpl/cache/settings_inc.object.tpl.php:15 -#: tpl/general/online.tpl.php:123 msgid "Enabled" msgstr "" #: tpl/cache/settings_inc.object.tpl.php:16 -#: tpl/general/online.tpl.php:125 msgid "Disabled" msgstr "" @@ -2972,8 +2987,8 @@ msgid "Use external object cache functionality." msgstr "" #: tpl/cache/settings_inc.object.tpl.php:52 -#: tpl/crawler/blacklist.tpl.php:35 -#: tpl/crawler/summary.tpl.php:138 +#: tpl/crawler/blacklist.tpl.php:42 +#: tpl/crawler/summary.tpl.php:153 msgid "Status" msgstr "" @@ -3043,729 +3058,753 @@ msgstr "" msgid "When enabled, the cache will automatically purge when any plugin, theme or the WordPress core is upgraded." msgstr "" -#: tpl/cdn/cf.tpl.php:11 +#: tpl/cdn/cf.tpl.php:17 msgid "Cloudflare Settings" msgstr "" -#: tpl/cdn/cf.tpl.php:25 +#: tpl/cdn/cf.tpl.php:31 msgid "Use %s API functionality." msgstr "" -#: tpl/cdn/cf.tpl.php:29 +#: tpl/cdn/cf.tpl.php:35 msgid "Global API Key / API Token" msgstr "" -#: tpl/cdn/cf.tpl.php:33 +#: tpl/cdn/cf.tpl.php:38 msgid "Your API key / token is used to access %s APIs." msgstr "" -#: tpl/cdn/cf.tpl.php:34 -msgid "Get it from %2$s." +#: tpl/cdn/cf.tpl.php:39 +msgid "Get it from %s." msgstr "" -#: tpl/cdn/cf.tpl.php:35 +#: tpl/cdn/cf.tpl.php:40 msgid "Recommended to generate the token from Cloudflare API token template \"WordPress\"." msgstr "" -#: tpl/cdn/cf.tpl.php:40 +#: tpl/cdn/cf.tpl.php:44 msgid "Email Address" msgstr "" -#: tpl/cdn/cf.tpl.php:44 +#: tpl/cdn/cf.tpl.php:47 msgid "Your Email address on %s." msgstr "" -#: tpl/cdn/cf.tpl.php:45 +#: tpl/cdn/cf.tpl.php:48 msgid "Optional when API token used." msgstr "" -#: tpl/cdn/cf.tpl.php:50 +#: tpl/cdn/cf.tpl.php:52 msgid "Domain" msgstr "" -#: tpl/cdn/cf.tpl.php:58 +#: tpl/cdn/cf.tpl.php:59 msgid "You can just type part of the domain." msgstr "" -#: tpl/cdn/cf.tpl.php:59 +#: tpl/cdn/cf.tpl.php:60 msgid "Once saved, it will be matched with the current list and completed automatically." msgstr "" -#: tpl/cdn/cf.tpl.php:73 +#: tpl/cdn/cf.tpl.php:74 msgid "Clear %s cache when \"Purge All\" is run." msgstr "" -#: tpl/cdn/cf.tpl.php:97 +#: tpl/cdn/cf.tpl.php:102 msgid "To enable the following functionality, turn ON Cloudflare API in CDN Settings." msgstr "" -#: tpl/cdn/cf.tpl.php:102 +#: tpl/cdn/cf.tpl.php:107 msgid "Cloudflare Domain" msgstr "" -#: tpl/cdn/cf.tpl.php:103 +#: tpl/cdn/cf.tpl.php:108 msgid "Cloudflare Zone" msgstr "" -#: tpl/cdn/cf.tpl.php:106 +#: tpl/cdn/cf.tpl.php:111 msgid "Development Mode" msgstr "" -#: tpl/cdn/cf.tpl.php:108 +#: tpl/cdn/cf.tpl.php:113 msgid "Turn ON" msgstr "" -#: tpl/cdn/cf.tpl.php:111 +#: tpl/cdn/cf.tpl.php:116 msgid "Turn OFF" msgstr "" -#: tpl/cdn/cf.tpl.php:114 +#: tpl/cdn/cf.tpl.php:119 msgid "Check Status" msgstr "" -#: tpl/cdn/cf.tpl.php:123 +#: tpl/cdn/cf.tpl.php:129 msgid "Current status is %1$s since %2$s." msgstr "" -#: tpl/cdn/cf.tpl.php:128 +#: tpl/cdn/cf.tpl.php:137 msgid "Current status is %s." msgstr "" -#: tpl/cdn/cf.tpl.php:129 +#: tpl/cdn/cf.tpl.php:141 msgid "Development mode will be automatically turned off in %s." msgstr "" -#: tpl/cdn/cf.tpl.php:137 +#: tpl/cdn/cf.tpl.php:149 msgid "Temporarily bypass Cloudflare cache. This allows changes to the origin server to be seen in realtime." msgstr "" -#: tpl/cdn/cf.tpl.php:138 +#: tpl/cdn/cf.tpl.php:151 msgid "Development Mode will be turned off automatically after three hours." msgstr "" -#: tpl/cdn/cf.tpl.php:144 +#: tpl/cdn/cf.tpl.php:152 +msgid "%1$sLearn More%2$s" +msgstr "" + +#: tpl/cdn/cf.tpl.php:156 msgid "Cloudflare Cache" msgstr "" -#: tpl/cdn/cf.tpl.php:150 +#: tpl/cdn/cf.tpl.php:162 msgid "Purge Everything" msgstr "" -#: tpl/cdn/entry.tpl.php:8 +#: tpl/cdn/entry.tpl.php:14 msgid "QUIC.cloud" msgstr "" -#: tpl/cdn/entry.tpl.php:10 +#: tpl/cdn/entry.tpl.php:16 msgid "Other Static CDN" msgstr "" -#: tpl/cdn/entry.tpl.php:17 +#: tpl/cdn/entry.tpl.php:22 msgid "LiteSpeed Cache CDN" msgstr "" -#: tpl/cdn/other.tpl.php:20 +#: tpl/cdn/other.tpl.php:28 msgid "CDN Settings" msgstr "" -#: tpl/cdn/other.tpl.php:34 -msgid "Turn this setting %1$s if you are using a traditional Content Delivery Network (CDN) or a subdomain for static content with QUIC.cloud CDN." +#: tpl/cdn/other.tpl.php:44 +msgid "Turn this setting %s if you are using a traditional Content Delivery Network (CDN) or a subdomain for static content with QUIC.cloud CDN." msgstr "" -#: tpl/cdn/other.tpl.php:39 -msgid "NOTE: QUIC.cloud CDN and Cloudflare do not use CDN Mapping. If you are are only using QUIC.cloud or Cloudflare, leave this setting %1$s." +#: tpl/cdn/other.tpl.php:52 +msgid "NOTE: QUIC.cloud CDN and Cloudflare do not use CDN Mapping. If you are only using QUIC.cloud or Cloudflare, leave this setting %s." msgstr "" -#: tpl/cdn/other.tpl.php:64 +#: tpl/cdn/other.tpl.php:80 msgid "To randomize CDN hostname, define multiple hostnames for the same resources." msgstr "" -#: tpl/cdn/other.tpl.php:69 +#: tpl/cdn/other.tpl.php:87 msgid "Serve all image files through the CDN. This will affect all attachments, HTML %1$s tags, and CSS %2$s attributes." msgstr "" -#: tpl/cdn/other.tpl.php:73 +#: tpl/cdn/other.tpl.php:94 msgid "Serve all CSS files through the CDN. This will affect all enqueued WP CSS files." msgstr "" -#: tpl/cdn/other.tpl.php:77 +#: tpl/cdn/other.tpl.php:97 msgid "Serve all JavaScript files through the CDN. This will affect all enqueued WP JavaScript files." msgstr "" -#: tpl/cdn/other.tpl.php:81 +#: tpl/cdn/other.tpl.php:100 msgid "Static file type links to be replaced by CDN links." msgstr "" -#: tpl/cdn/other.tpl.php:83 -msgid "This will affect all tags containing attributes: %1$s %2$s %3$s." +#: tpl/cdn/other.tpl.php:104 +msgid "This will affect all tags containing attributes: %s." msgstr "" -#: tpl/cdn/other.tpl.php:87 +#: tpl/cdn/other.tpl.php:112 msgid "If you turn any of the above settings OFF, please remove the related file types from the %s box." msgstr "" -#: tpl/cdn/other.tpl.php:111 +#: tpl/cdn/other.tpl.php:136 msgid "Specify which HTML element attributes will be replaced with CDN Mapping." msgstr "" -#: tpl/cdn/other.tpl.php:112 -#: tpl/img_optm/settings.tpl.php:118 +#: tpl/cdn/other.tpl.php:137 +#: tpl/img_optm/settings.tpl.php:150 msgid "Only attributes listed here will be replaced." msgstr "" -#: tpl/cdn/other.tpl.php:113 -#: tpl/img_optm/settings.tpl.php:119 +#: tpl/cdn/other.tpl.php:141 +#: tpl/img_optm/settings.tpl.php:151 msgid "Use the format %1$s or %2$s (element is optional)." msgstr "" -#: tpl/cdn/other.tpl.php:127 +#: tpl/cdn/other.tpl.php:161 msgid "Site URL to be served through the CDN. Beginning with %1$s. For example, %2$s." msgstr "" -#: tpl/cdn/other.tpl.php:150 +#: tpl/cdn/other.tpl.php:196 msgid "Only files within these directories will be pointed to the CDN." msgstr "" -#: tpl/cdn/other.tpl.php:164 +#: tpl/cdn/other.tpl.php:210 msgid "Paths containing these strings will not be served from the CDN." msgstr "" -#: tpl/cdn/qc.tpl.php:20 -#: tpl/dash/dashboard.tpl.php:865 +#: tpl/cdn/qc.tpl.php:24 +#: tpl/dash/dashboard.tpl.php:885 msgid "Refresh Status" msgstr "" -#: tpl/cdn/qc.tpl.php:23 +#: tpl/cdn/qc.tpl.php:27 msgid "QUIC.cloud CDN Status Overview" msgstr "" -#: tpl/cdn/qc.tpl.php:25 +#: tpl/cdn/qc.tpl.php:29 msgid "Check the status of your most important settings and the health of your CDN setup here." msgstr "" -#: tpl/cdn/qc.tpl.php:30 -#: tpl/dash/dashboard.tpl.php:142 +#: tpl/cdn/qc.tpl.php:34 +#: tpl/dash/dashboard.tpl.php:146 msgid "Accelerate, Optimize, Protect" msgstr "" -#: tpl/cdn/qc.tpl.php:31 -#: tpl/dash/dashboard.tpl.php:145 +#: tpl/cdn/qc.tpl.php:36 +#: tpl/dash/dashboard.tpl.php:150 msgid "Speed up your WordPress site even further with QUIC.cloud Online Services and CDN." msgstr "" -#: tpl/cdn/qc.tpl.php:32 -#: tpl/general/online.tpl.php:46 -#: tpl/general/online.tpl.php:132 +#: tpl/cdn/qc.tpl.php:38 +#: tpl/general/online.tpl.php:61 +#: tpl/general/online.tpl.php:145 msgid "Free monthly quota available." msgstr "" -#: tpl/cdn/qc.tpl.php:33 -#: tpl/dash/dashboard.tpl.php:152 -#: tpl/general/online.tpl.php:49 -#: tpl/general/online.tpl.php:105 +#: tpl/cdn/qc.tpl.php:41 +#: tpl/dash/dashboard.tpl.php:158 +#: tpl/general/online.tpl.php:64 +#: tpl/general/online.tpl.php:119 msgid "Enable QUIC.cloud services" msgstr "" -#: tpl/cdn/qc.tpl.php:35 -#: tpl/dash/dashboard.tpl.php:158 -#: tpl/general/online.tpl.php:19 +#: tpl/cdn/qc.tpl.php:45 +#: tpl/dash/dashboard.tpl.php:166 +#: tpl/general/online.tpl.php:26 msgid "QUIC.cloud provides CDN and online optimization services, and is not required. You may use many features of this plugin without QUIC.cloud." msgstr "" -#: tpl/cdn/qc.tpl.php:36 -#: tpl/dash/dashboard.tpl.php:160 +#: tpl/cdn/qc.tpl.php:46 +#: tpl/dash/dashboard.tpl.php:168 msgid "Learn More about QUIC.cloud" msgstr "" -#: tpl/cdn/qc.tpl.php:43 +#: tpl/cdn/qc.tpl.php:53 msgid "QUIC.cloud CDN is currently fully disabled." msgstr "" -#: tpl/cdn/qc.tpl.php:45 +#: tpl/cdn/qc.tpl.php:55 msgid "QUIC.cloud CDN is not available for anonymous (unlinked) users." msgstr "" -#: tpl/cdn/qc.tpl.php:49 +#: tpl/cdn/qc.tpl.php:59 msgid "Link & Enable QUIC.cloud CDN" msgstr "" -#: tpl/cdn/qc.tpl.php:51 -#: tpl/dash/dashboard.tpl.php:842 +#: tpl/cdn/qc.tpl.php:61 +#: tpl/dash/dashboard.tpl.php:856 msgid "Enable QUIC.cloud CDN" msgstr "" -#: tpl/cdn/qc.tpl.php:63 +#: tpl/cdn/qc.tpl.php:71 msgid "Content Delivery Network Service" msgstr "" -#: tpl/cdn/qc.tpl.php:64 +#: tpl/cdn/qc.tpl.php:73 +msgid "Serve your visitors fast" +msgstr "" + +#: tpl/cdn/qc.tpl.php:73 msgid "no matter where they live." msgstr "" -#: tpl/cdn/qc.tpl.php:66 -msgid "Best available WordPress performance, globally fast TTFB, easy setup, and more!" +#. translators: %s: Link tags +#: tpl/cdn/qc.tpl.php:79 +msgid "Best available WordPress performance, globally fast TTFB, easy setup, and %smore%s!" msgstr "" -#: tpl/cdn/qc.tpl.php:80 +#: tpl/cdn/qc.tpl.php:96 msgid "QUIC.cloud CDN Options" msgstr "" -#: tpl/cdn/qc.tpl.php:98 +#: tpl/cdn/qc.tpl.php:117 msgid "To manage your QUIC.cloud options, go to your hosting provider's portal." msgstr "" -#: tpl/cdn/qc.tpl.php:100 +#: tpl/cdn/qc.tpl.php:119 msgid "To manage your QUIC.cloud options, please contact your hosting provider." msgstr "" -#: tpl/cdn/qc.tpl.php:104 -#: tpl/cdn/qc.tpl.php:112 +#: tpl/cdn/qc.tpl.php:123 +#: tpl/cdn/qc.tpl.php:143 msgid "To manage your QUIC.cloud options, go to QUIC.cloud Dashboard." msgstr "" -#: tpl/cdn/qc.tpl.php:105 -#: tpl/cdn/qc.tpl.php:108 -#: tpl/dash/dashboard.tpl.php:357 -#: tpl/general/online.tpl.php:140 +#: tpl/cdn/qc.tpl.php:126 +#: tpl/cdn/qc.tpl.php:133 +#: tpl/dash/dashboard.tpl.php:359 +#: tpl/general/online.tpl.php:153 msgid "Link to QUIC.cloud" msgstr "" -#: tpl/cdn/qc.tpl.php:107 +#: tpl/cdn/qc.tpl.php:130 msgid "You are currently using services as an anonymous user. To manage your QUIC.cloud options, use the button below to create an account and link to the QUIC.cloud Dashboard." msgstr "" -#: tpl/cdn/qc.tpl.php:110 -#: tpl/cdn/qc.tpl.php:113 +#: tpl/cdn/qc.tpl.php:139 +#: tpl/cdn/qc.tpl.php:146 msgid "My QUIC.cloud Dashboard" msgstr "" -#: tpl/crawler/blacklist.tpl.php:16 +#: tpl/crawler/blacklist.tpl.php:22 msgid "Are you sure to delete all existing blocklist items?" msgstr "" -#: tpl/crawler/blacklist.tpl.php:17 +#: tpl/crawler/blacklist.tpl.php:23 msgid "Empty blocklist" msgstr "" -#: tpl/crawler/blacklist.tpl.php:22 -#: tpl/crawler/entry.tpl.php:10 +#: tpl/crawler/blacklist.tpl.php:28 +#: tpl/crawler/entry.tpl.php:16 msgid "Blocklist" msgstr "" -#: tpl/crawler/blacklist.tpl.php:26 -#: tpl/img_optm/summary.tpl.php:195 +#: tpl/crawler/blacklist.tpl.php:32 +#: tpl/img_optm/summary.tpl.php:201 msgid "Total" msgstr "" -#: tpl/crawler/blacklist.tpl.php:34 -#: tpl/crawler/map.tpl.php:67 -#: tpl/toolbox/purge.tpl.php:238 +#: tpl/crawler/blacklist.tpl.php:41 +#: tpl/crawler/map.tpl.php:76 +#: tpl/toolbox/purge.tpl.php:200 msgid "URL" msgstr "" -#: tpl/crawler/blacklist.tpl.php:36 -#: tpl/crawler/map.tpl.php:69 +#: tpl/crawler/blacklist.tpl.php:43 +#: tpl/crawler/map.tpl.php:78 msgid "Operation" msgstr "" -#: tpl/crawler/blacklist.tpl.php:49 +#: tpl/crawler/blacklist.tpl.php:54 msgid "Remove from Blocklist" msgstr "" -#: tpl/crawler/blacklist.tpl.php:59 -msgid "PHP Constant %s available to disable blocklist." +#: tpl/crawler/blacklist.tpl.php:69 +msgid "API: PHP Constant %s available to disable blocklist." msgstr "" -#: tpl/crawler/blacklist.tpl.php:62 -msgid "Filter %s available to disable blocklist." +#: tpl/crawler/blacklist.tpl.php:79 +msgid "API: Filter %s available to disable blocklist." msgstr "" -#: tpl/crawler/blacklist.tpl.php:65 +#: tpl/crawler/blacklist.tpl.php:87 msgid "Not blocklisted" msgstr "" -#: tpl/crawler/blacklist.tpl.php:66 -#: tpl/crawler/map.tpl.php:96 +#: tpl/crawler/blacklist.tpl.php:88 +#: tpl/crawler/map.tpl.php:103 msgid "Blocklisted due to not cacheable" msgstr "" -#: tpl/crawler/blacklist.tpl.php:67 -#: tpl/crawler/map.tpl.php:53 -#: tpl/crawler/map.tpl.php:97 -#: tpl/crawler/summary.tpl.php:175 -#: tpl/crawler/summary.tpl.php:210 +#: tpl/crawler/blacklist.tpl.php:89 +#: tpl/crawler/map.tpl.php:64 +#: tpl/crawler/map.tpl.php:104 +#: tpl/crawler/summary.tpl.php:199 +#: tpl/crawler/summary.tpl.php:247 msgid "Blocklisted" msgstr "" -#: tpl/crawler/entry.tpl.php:8 +#: tpl/crawler/entry.tpl.php:14 msgid "Summary" msgstr "" -#: tpl/crawler/entry.tpl.php:9 +#: tpl/crawler/entry.tpl.php:15 msgid "Map" msgstr "" -#: tpl/crawler/entry.tpl.php:18 +#: tpl/crawler/entry.tpl.php:23 msgid "LiteSpeed Cache Crawler" msgstr "" -#: tpl/crawler/map.tpl.php:19 +#: tpl/crawler/map.tpl.php:29 msgid "Clean Crawler Map" msgstr "" -#: tpl/crawler/map.tpl.php:23 +#: tpl/crawler/map.tpl.php:32 msgid "Refresh Crawler Map" msgstr "" -#: tpl/crawler/map.tpl.php:30 +#: tpl/crawler/map.tpl.php:40 msgid "Generated at %s" msgstr "" -#: tpl/crawler/map.tpl.php:36 +#: tpl/crawler/map.tpl.php:48 msgid "Sitemap List" msgstr "" -#: tpl/crawler/map.tpl.php:40 +#: tpl/crawler/map.tpl.php:52 msgid "Sitemap Total" msgstr "" -#: tpl/crawler/map.tpl.php:51 -#: tpl/crawler/map.tpl.php:94 +#: tpl/crawler/map.tpl.php:58 +msgid "URL Search" +msgstr "" + +#: tpl/crawler/map.tpl.php:62 +#: tpl/crawler/map.tpl.php:101 msgid "Cache Hit" msgstr "" -#: tpl/crawler/map.tpl.php:52 -#: tpl/crawler/map.tpl.php:95 +#: tpl/crawler/map.tpl.php:63 +#: tpl/crawler/map.tpl.php:102 msgid "Cache Miss" msgstr "" -#: tpl/crawler/map.tpl.php:68 -#: tpl/dash/dashboard.tpl.php:76 -#: tpl/dash/dashboard.tpl.php:781 +#: tpl/crawler/map.tpl.php:77 +#: tpl/dash/dashboard.tpl.php:80 +#: tpl/dash/dashboard.tpl.php:799 msgid "Crawler Status" msgstr "" -#: tpl/crawler/map.tpl.php:83 +#: tpl/crawler/map.tpl.php:89 msgid "Add to Blocklist" msgstr "" -#: tpl/crawler/settings.tpl.php:11 +#: tpl/crawler/settings.tpl.php:17 msgid "Crawler General Settings" msgstr "" -#: tpl/crawler/settings.tpl.php:25 +#: tpl/crawler/settings.tpl.php:31 msgid "This will enable crawler cron." msgstr "" -#: tpl/crawler/settings.tpl.php:39 +#: tpl/crawler/settings.tpl.php:45 msgid "Specify how long in seconds before the crawler should initiate crawling the entire sitemap again." msgstr "" -#: tpl/crawler/settings.tpl.php:53 +#: tpl/crawler/settings.tpl.php:59 msgid "The crawler will use your XML sitemap or sitemap index. Enter the full URL to your sitemap here." msgstr "" -#: tpl/crawler/settings.tpl.php:68 +#: tpl/crawler/settings.tpl.php:73 msgid "The maximum average server load allowed while crawling. The number of crawler threads in use will be actively reduced until average server load falls under this limit. If this cannot be achieved with a single thread, the current crawler run will be terminated." msgstr "" -#: tpl/crawler/settings.tpl.php:74 -msgid "Server enforced value" +#: tpl/crawler/settings.tpl.php:79 +msgid "Server enforced value: %s" msgstr "" -#: tpl/crawler/settings.tpl.php:79 -msgid "Server allowed max value" +#: tpl/crawler/settings.tpl.php:89 +msgid "Server allowed max value: %s" msgstr "" -#: tpl/crawler/settings.tpl.php:98 +#: tpl/crawler/settings.tpl.php:109 msgid "To crawl the site as a logged-in user, enter the user ids to be simulated." msgstr "" -#: tpl/crawler/settings.tpl.php:105 -#: tpl/crawler/summary.tpl.php:184 +#: tpl/crawler/settings.tpl.php:116 +#: tpl/crawler/summary.tpl.php:211 msgid "You must set %s before using this feature." msgstr "" -#: tpl/crawler/settings.tpl.php:114 +#: tpl/crawler/settings.tpl.php:136 msgid "You must set %1$s to %2$s before using this feature." msgstr "" -#: tpl/crawler/settings.tpl.php:142 +#: tpl/crawler/settings.tpl.php:172 msgid "To crawl for a particular cookie, enter the cookie name, and the values you wish to crawl for. Values should be one per line. There will be one crawler created per cookie value, per simulated role." msgstr "" -#: tpl/crawler/settings.tpl.php:144 +#: tpl/crawler/settings.tpl.php:177 msgid "Use %1$s in %2$s to indicate this cookie has not been set." msgstr "" -#: tpl/crawler/summary.tpl.php:21 +#: tpl/crawler/summary.tpl.php:28 msgid "You need to set the %s in Settings first before using the crawler" msgstr "" -#: tpl/crawler/summary.tpl.php:47 +#: tpl/crawler/summary.tpl.php:54 msgid "Crawler Cron" msgstr "" -#: tpl/crawler/summary.tpl.php:54 +#: tpl/crawler/summary.tpl.php:61 msgid "The crawler feature is not enabled on the LiteSpeed server. Please consult your server admin or hosting provider." msgstr "" -#: tpl/crawler/summary.tpl.php:55 -msgid "See Introduction for Enabling the Crawler for detailed information." +#. translators: %s: Link tags +#: tpl/crawler/summary.tpl.php:66 +msgid "See %sIntroduction for Enabling the Crawler%s for detailed information." msgstr "" -#: tpl/crawler/summary.tpl.php:62 +#: tpl/crawler/summary.tpl.php:77 msgid "Current sitemap crawl started at" msgstr "" -#: tpl/crawler/summary.tpl.php:68 +#: tpl/crawler/summary.tpl.php:82 msgid "The next complete sitemap crawl will start at" msgstr "" -#: tpl/crawler/summary.tpl.php:76 +#: tpl/crawler/summary.tpl.php:90 msgid "Last complete run time for all crawlers" msgstr "" -#: tpl/crawler/summary.tpl.php:77 -#: tpl/crawler/summary.tpl.php:84 +#: tpl/crawler/summary.tpl.php:91 +#: tpl/crawler/summary.tpl.php:98 msgid "%d seconds" msgstr "" -#: tpl/crawler/summary.tpl.php:83 +#: tpl/crawler/summary.tpl.php:97 msgid "Run time for previous crawler" msgstr "" -#: tpl/crawler/summary.tpl.php:90 -#: tpl/dash/dashboard.tpl.php:89 -#: tpl/dash/dashboard.tpl.php:794 +#: tpl/crawler/summary.tpl.php:104 +#: tpl/dash/dashboard.tpl.php:91 +#: tpl/dash/dashboard.tpl.php:810 msgid "Current crawler started at" msgstr "" -#: tpl/crawler/summary.tpl.php:96 +#: tpl/crawler/summary.tpl.php:110 msgid "Current server load" msgstr "" -#: tpl/crawler/summary.tpl.php:102 -#: tpl/dash/dashboard.tpl.php:96 -#: tpl/dash/dashboard.tpl.php:801 +#: tpl/crawler/summary.tpl.php:116 +#: tpl/dash/dashboard.tpl.php:97 +#: tpl/dash/dashboard.tpl.php:816 msgid "Last interval" msgstr "" -#: tpl/crawler/summary.tpl.php:109 +#: tpl/crawler/summary.tpl.php:123 #: tpl/dash/dashboard.tpl.php:103 -#: tpl/dash/dashboard.tpl.php:808 +#: tpl/dash/dashboard.tpl.php:822 msgid "Ended reason" msgstr "" -#: tpl/crawler/summary.tpl.php:116 -msgid "Last crawled: %s item(s)" +#: tpl/crawler/summary.tpl.php:130 +msgid "Last crawled" +msgstr "" + +#: tpl/crawler/summary.tpl.php:133 +msgid "%d item(s)" msgstr "" -#: tpl/crawler/summary.tpl.php:122 +#: tpl/crawler/summary.tpl.php:141 msgid "Reset position" msgstr "" -#: tpl/crawler/summary.tpl.php:125 +#: tpl/crawler/summary.tpl.php:142 msgid "Manually run" msgstr "" -#: tpl/crawler/summary.tpl.php:136 +#: tpl/crawler/summary.tpl.php:151 msgid "Cron Name" msgstr "" -#: tpl/crawler/summary.tpl.php:137 +#: tpl/crawler/summary.tpl.php:152 msgid "Run Frequency" msgstr "" -#: tpl/crawler/summary.tpl.php:139 +#: tpl/crawler/summary.tpl.php:154 msgid "Activate" msgstr "" -#: tpl/crawler/summary.tpl.php:140 +#: tpl/crawler/summary.tpl.php:155 msgid "Running" msgstr "" -#: tpl/crawler/summary.tpl.php:172 +#: tpl/crawler/summary.tpl.php:184 msgid "Waiting" msgstr "" -#: tpl/crawler/summary.tpl.php:173 +#: tpl/crawler/summary.tpl.php:189 msgid "Hit" msgstr "" -#: tpl/crawler/summary.tpl.php:174 +#: tpl/crawler/summary.tpl.php:194 msgid "Miss" msgstr "" -#: tpl/crawler/summary.tpl.php:195 +#: tpl/crawler/summary.tpl.php:230 +msgid "Position: " +msgstr "" + +#: tpl/crawler/summary.tpl.php:232 msgid "running" msgstr "" -#: tpl/crawler/summary.tpl.php:207 +#: tpl/crawler/summary.tpl.php:244 msgid "Waiting to be Crawled" msgstr "" -#: tpl/crawler/summary.tpl.php:208 +#: tpl/crawler/summary.tpl.php:245 msgid "Already Cached" msgstr "" -#: tpl/crawler/summary.tpl.php:209 +#: tpl/crawler/summary.tpl.php:246 msgid "Successfully Crawled" msgstr "" -#: tpl/crawler/summary.tpl.php:214 +#: tpl/crawler/summary.tpl.php:251 msgid "Run frequency is set by the Interval Between Runs setting." msgstr "" -#: tpl/crawler/summary.tpl.php:217 -msgid "Crawlers cannot run concurrently." +#: tpl/crawler/summary.tpl.php:254 +msgid "Crawlers cannot run concurrently. If both the cron and a manual run start at similar times, the first to be started will take precedence." msgstr "" -#: tpl/crawler/summary.tpl.php:218 -msgid " If both the cron and a manual run start at similar times, the first to be started will take precedence." -msgstr "" - -#: tpl/crawler/summary.tpl.php:221 -msgid "Please see Hooking WP-Cron Into the System Task Scheduler to learn how to create the system cron task." +#. translators: %s: Link tags +#: tpl/crawler/summary.tpl.php:261 +msgid "Please see %sHooking WP-Cron Into the System Task Scheduler%s to learn how to create the system cron task." msgstr "" -#: tpl/crawler/summary.tpl.php:226 +#: tpl/crawler/summary.tpl.php:272 msgid "Watch Crawler Status" msgstr "" -#: tpl/crawler/summary.tpl.php:233 +#: tpl/crawler/summary.tpl.php:278 msgid "Show crawler status" msgstr "" -#: tpl/crawler/summary.tpl.php:251 +#: tpl/crawler/summary.tpl.php:288 +msgid "Start watching..." +msgstr "" + +#: tpl/crawler/summary.tpl.php:293 msgid "No crawler meta file generated yet" msgstr "" -#: tpl/dash/dashboard.tpl.php:48 -#: tpl/dash/dashboard.tpl.php:605 +#: tpl/dash/dashboard.tpl.php:53 +#: tpl/dash/dashboard.tpl.php:596 msgid "Cache Status" msgstr "" -#: tpl/dash/dashboard.tpl.php:49 -#: tpl/dash/dashboard.tpl.php:77 -#: tpl/dash/dashboard.tpl.php:519 -#: tpl/dash/dashboard.tpl.php:606 -#: tpl/dash/dashboard.tpl.php:634 -#: tpl/dash/dashboard.tpl.php:671 -#: tpl/dash/dashboard.tpl.php:708 -#: tpl/dash/dashboard.tpl.php:745 -#: tpl/dash/dashboard.tpl.php:782 -#: tpl/dash/dashboard.tpl.php:833 +#: tpl/dash/dashboard.tpl.php:54 +#: tpl/dash/dashboard.tpl.php:81 +#: tpl/dash/dashboard.tpl.php:520 +#: tpl/dash/dashboard.tpl.php:597 +#: tpl/dash/dashboard.tpl.php:624 +#: tpl/dash/dashboard.tpl.php:668 +#: tpl/dash/dashboard.tpl.php:712 +#: tpl/dash/dashboard.tpl.php:756 +#: tpl/dash/dashboard.tpl.php:800 +#: tpl/dash/dashboard.tpl.php:847 msgid "More" msgstr "" -#: tpl/dash/dashboard.tpl.php:54 -#: tpl/dash/dashboard.tpl.php:611 +#: tpl/dash/dashboard.tpl.php:58 +#: tpl/dash/dashboard.tpl.php:601 msgid "Public Cache" msgstr "" -#: tpl/dash/dashboard.tpl.php:55 -#: tpl/dash/dashboard.tpl.php:612 +#: tpl/dash/dashboard.tpl.php:59 +#: tpl/dash/dashboard.tpl.php:602 msgid "Private Cache" msgstr "" -#: tpl/dash/dashboard.tpl.php:81 -#: tpl/dash/dashboard.tpl.php:786 +#: tpl/dash/dashboard.tpl.php:84 +#: tpl/dash/dashboard.tpl.php:803 msgid "Crawler(s)" msgstr "" -#: tpl/dash/dashboard.tpl.php:84 -#: tpl/dash/dashboard.tpl.php:789 +#: tpl/dash/dashboard.tpl.php:87 +#: tpl/dash/dashboard.tpl.php:806 msgid "Currently active crawler" msgstr "" -#: tpl/dash/dashboard.tpl.php:110 -#: tpl/dash/dashboard.tpl.php:815 -msgid "Last crawled: %d item(s)" +#: tpl/dash/dashboard.tpl.php:111 +#: tpl/dash/dashboard.tpl.php:830 +msgid "%1$s %2$d item(s)" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:112 +#: tpl/dash/dashboard.tpl.php:831 +msgid "Last crawled:" msgstr "" -#: tpl/dash/dashboard.tpl.php:122 -#: tpl/dash/dashboard.tpl.php:883 +#: tpl/dash/dashboard.tpl.php:128 +#: tpl/dash/dashboard.tpl.php:907 msgid "News" msgstr "" -#: tpl/dash/dashboard.tpl.php:147 +#: tpl/dash/dashboard.tpl.php:153 msgid "Free monthly quota available. Can also be used anonymously (no email required)." msgstr "" -#: tpl/dash/dashboard.tpl.php:154 +#: tpl/dash/dashboard.tpl.php:162 msgid "Do not show this again" msgstr "" -#: tpl/dash/dashboard.tpl.php:172 +#: tpl/dash/dashboard.tpl.php:179 msgid "QUIC.cloud Service Usage Statistics" msgstr "" -#: tpl/dash/dashboard.tpl.php:174 +#: tpl/dash/dashboard.tpl.php:181 msgid "Refresh Usage" msgstr "" -#: tpl/dash/dashboard.tpl.php:175 +#: tpl/dash/dashboard.tpl.php:182 msgid "Sync data from Cloud" msgstr "" -#: tpl/dash/dashboard.tpl.php:183 -msgid "The features below are provided by" +#: tpl/dash/dashboard.tpl.php:193 +msgid "The features below are provided by %s" msgstr "" -#: tpl/dash/dashboard.tpl.php:192 -#: tpl/dash/network_dash.tpl.php:30 +#: tpl/dash/dashboard.tpl.php:205 +#: tpl/dash/network_dash.tpl.php:38 msgid "CDN Bandwidth" msgstr "" -#: tpl/dash/dashboard.tpl.php:193 -#: tpl/dash/dashboard.tpl.php:707 -#: tpl/dash/network_dash.tpl.php:31 +#: tpl/dash/dashboard.tpl.php:206 +#: tpl/dash/dashboard.tpl.php:711 +#: tpl/dash/network_dash.tpl.php:39 msgid "Low Quality Image Placeholder" msgstr "" -#: tpl/dash/dashboard.tpl.php:251 -#: tpl/dash/network_dash.tpl.php:79 +#: tpl/dash/dashboard.tpl.php:258 +#: tpl/dash/network_dash.tpl.php:95 msgid "Fast Queue Usage" msgstr "" -#: tpl/dash/dashboard.tpl.php:251 -#: tpl/dash/network_dash.tpl.php:79 +#: tpl/dash/dashboard.tpl.php:258 +#: tpl/dash/network_dash.tpl.php:95 msgid "Usage" msgstr "" -#: tpl/dash/dashboard.tpl.php:264 -#: tpl/dash/network_dash.tpl.php:91 +#: tpl/dash/dashboard.tpl.php:270 +#: tpl/dash/network_dash.tpl.php:108 msgid "PAYG Balance" msgstr "" -#: tpl/dash/dashboard.tpl.php:265 -msgid "PAYG used this month" +#: tpl/dash/dashboard.tpl.php:271 +msgid "PAYG used this month: %s. PAYG balance and usage not included in above quota calculation." msgstr "" -#: tpl/dash/dashboard.tpl.php:265 -msgid "PAYG balance and usage not included in above quota calculation." -msgstr "" - -#: tpl/dash/dashboard.tpl.php:267 -#: tpl/dash/network_dash.tpl.php:94 +#: tpl/dash/dashboard.tpl.php:273 +#: tpl/dash/network_dash.tpl.php:111 msgid "Pay as You Go Usage Statistics" msgstr "" -#: tpl/dash/dashboard.tpl.php:292 -#: tpl/dash/network_dash.tpl.php:101 +#: tpl/dash/dashboard.tpl.php:291 +#: tpl/dash/network_dash.tpl.php:118 msgid "Total Usage" msgstr "" -#: tpl/dash/dashboard.tpl.php:293 -#: tpl/dash/network_dash.tpl.php:102 +#: tpl/dash/dashboard.tpl.php:292 +#: tpl/dash/network_dash.tpl.php:119 msgid "Total images optimized in this month" msgstr "" -#: tpl/dash/dashboard.tpl.php:302 +#: tpl/dash/dashboard.tpl.php:300 msgid "Remaining Daily Quota" msgstr "" -#: tpl/dash/dashboard.tpl.php:313 +#: tpl/dash/dashboard.tpl.php:310 msgid "Partner Benefits Provided by" msgstr "" @@ -3773,2096 +3812,2258 @@ msgstr "" msgid "Enable QUIC.cloud Services" msgstr "" -#: tpl/dash/dashboard.tpl.php:352 -#: tpl/general/online.tpl.php:115 +#: tpl/dash/dashboard.tpl.php:353 +#: tpl/general/online.tpl.php:128 msgid "Go to QUIC.cloud dashboard" msgstr "" -#: tpl/dash/dashboard.tpl.php:378 -msgid "Current closest Cloud server is %s. Click to redetect." +#: tpl/dash/dashboard.tpl.php:381 +#: tpl/img_optm/summary.tpl.php:54 +#: tpl/page_optm/settings_css.tpl.php:111 +#: tpl/page_optm/settings_css.tpl.php:248 +#: tpl/page_optm/settings_media.tpl.php:194 +#: tpl/page_optm/settings_vpi.tpl.php:59 +msgid "Current closest Cloud server is %s. Click to redetect." msgstr "" -#: tpl/dash/dashboard.tpl.php:378 -#: tpl/img_optm/summary.tpl.php:46 -#: tpl/page_optm/settings_css.tpl.php:106 -#: tpl/page_optm/settings_css.tpl.php:250 -#: tpl/page_optm/settings_media.tpl.php:184 -#: tpl/page_optm/settings_vpi.tpl.php:53 +#: tpl/dash/dashboard.tpl.php:382 +#: tpl/img_optm/summary.tpl.php:54 +#: tpl/page_optm/settings_css.tpl.php:111 +#: tpl/page_optm/settings_css.tpl.php:248 +#: tpl/page_optm/settings_media.tpl.php:194 +#: tpl/page_optm/settings_vpi.tpl.php:59 msgid "Are you sure you want to redetect the closest cloud server for this service?" msgstr "" -#: tpl/dash/dashboard.tpl.php:378 -#: tpl/general/online.tpl.php:24 -#: tpl/img_optm/summary.tpl.php:46 -#: tpl/img_optm/summary.tpl.php:48 -#: tpl/page_optm/settings_css.tpl.php:106 -#: tpl/page_optm/settings_css.tpl.php:250 -#: tpl/page_optm/settings_media.tpl.php:184 -#: tpl/page_optm/settings_vpi.tpl.php:53 +#: tpl/dash/dashboard.tpl.php:384 +#: tpl/general/online.tpl.php:31 +#: tpl/img_optm/summary.tpl.php:54 +#: tpl/img_optm/summary.tpl.php:56 +#: tpl/page_optm/settings_css.tpl.php:111 +#: tpl/page_optm/settings_css.tpl.php:248 +#: tpl/page_optm/settings_media.tpl.php:194 +#: tpl/page_optm/settings_vpi.tpl.php:59 msgid "Redetect" msgstr "" -#: tpl/dash/dashboard.tpl.php:415 +#: tpl/dash/dashboard.tpl.php:418 msgid "You must be using one of the following products in order to measure Page Load Time:" msgstr "" -#: tpl/dash/dashboard.tpl.php:434 -#: tpl/dash/dashboard.tpl.php:503 -#: tpl/dash/dashboard.tpl.php:662 -#: tpl/dash/dashboard.tpl.php:699 -#: tpl/dash/dashboard.tpl.php:736 -#: tpl/dash/dashboard.tpl.php:773 -msgid "Last requested" +#: tpl/dash/dashboard.tpl.php:419 +msgid "LiteSpeed Web Server" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:421 +msgid "OpenLiteSpeed Web Server" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:423 +msgid "LiteSpeed Web ADC" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:425 +#: tpl/dash/dashboard.tpl.php:843 +msgid "QUIC.cloud CDN" msgstr "" -#: tpl/dash/dashboard.tpl.php:440 -#: tpl/dash/dashboard.tpl.php:508 +#: tpl/dash/dashboard.tpl.php:437 +#: tpl/dash/dashboard.tpl.php:502 +msgid "Requested: %s ago" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:445 +#: tpl/dash/dashboard.tpl.php:510 msgid "Refresh" msgstr "" -#: tpl/dash/dashboard.tpl.php:441 +#: tpl/dash/dashboard.tpl.php:446 msgid "Refresh page load time" msgstr "" -#: tpl/dash/dashboard.tpl.php:509 +#: tpl/dash/dashboard.tpl.php:511 msgid "Refresh page score" msgstr "" -#: tpl/dash/dashboard.tpl.php:518 -#: tpl/img_optm/entry.tpl.php:8 +#: tpl/dash/dashboard.tpl.php:519 +#: tpl/img_optm/entry.tpl.php:16 msgid "Image Optimization Summary" msgstr "" -#: tpl/dash/dashboard.tpl.php:538 -#: tpl/img_optm/summary.tpl.php:68 -#: tpl/img_optm/summary.tpl.php:81 +#: tpl/dash/dashboard.tpl.php:536 +#: tpl/img_optm/summary.tpl.php:76 +#: tpl/img_optm/summary.tpl.php:89 msgid "Send Optimization Request" msgstr "" -#: tpl/dash/dashboard.tpl.php:544 -#: tpl/img_optm/summary.tpl.php:308 +#: tpl/dash/dashboard.tpl.php:542 +#: tpl/img_optm/summary.tpl.php:316 msgid "Total Reduction" msgstr "" -#: tpl/dash/dashboard.tpl.php:547 -#: tpl/img_optm/summary.tpl.php:311 +#: tpl/dash/dashboard.tpl.php:545 +#: tpl/img_optm/summary.tpl.php:319 msgid "Images Pulled" msgstr "" -#: tpl/dash/dashboard.tpl.php:575 -#: tpl/img_optm/summary.tpl.php:314 +#: tpl/dash/dashboard.tpl.php:568 +#: tpl/img_optm/summary.tpl.php:322 msgid "Last Request" msgstr "" -#: tpl/dash/dashboard.tpl.php:578 +#: tpl/dash/dashboard.tpl.php:571 msgid "Last Pull" msgstr "" -#: tpl/dash/dashboard.tpl.php:633 -#: tpl/toolbox/purge.tpl.php:62 +#: tpl/dash/dashboard.tpl.php:623 +#: tpl/toolbox/purge.tpl.php:73 msgid "Critical CSS" msgstr "" -#: tpl/dash/dashboard.tpl.php:639 -#: tpl/dash/dashboard.tpl.php:676 -#: tpl/dash/dashboard.tpl.php:713 -#: tpl/dash/dashboard.tpl.php:750 -#: tpl/page_optm/settings_css.tpl.php:97 -#: tpl/page_optm/settings_css.tpl.php:241 -#: tpl/page_optm/settings_media.tpl.php:178 -#: tpl/page_optm/settings_vpi.tpl.php:47 -msgid "Last generated" +#: tpl/dash/dashboard.tpl.php:630 +#: tpl/dash/dashboard.tpl.php:674 +#: tpl/dash/dashboard.tpl.php:718 +#: tpl/dash/dashboard.tpl.php:762 +msgid "Last generated: %s" msgstr "" -#: tpl/dash/dashboard.tpl.php:642 -#: tpl/dash/dashboard.tpl.php:679 -#: tpl/dash/dashboard.tpl.php:716 -#: tpl/dash/dashboard.tpl.php:753 -msgid "Time to execute previous request" +#: tpl/dash/dashboard.tpl.php:638 +#: tpl/dash/dashboard.tpl.php:682 +#: tpl/dash/dashboard.tpl.php:726 +#: tpl/dash/dashboard.tpl.php:770 +msgid "Time to execute previous request: %s" msgstr "" -#: tpl/dash/dashboard.tpl.php:647 -#: tpl/dash/dashboard.tpl.php:684 -#: tpl/dash/dashboard.tpl.php:721 -#: tpl/dash/dashboard.tpl.php:758 +#: tpl/dash/dashboard.tpl.php:645 +#: tpl/dash/dashboard.tpl.php:689 +#: tpl/dash/dashboard.tpl.php:733 +#: tpl/dash/dashboard.tpl.php:777 msgid "Requests in queue" msgstr "" -#: tpl/dash/dashboard.tpl.php:654 -#: tpl/dash/dashboard.tpl.php:691 -#: tpl/dash/dashboard.tpl.php:728 -#: tpl/dash/dashboard.tpl.php:765 +#: tpl/dash/dashboard.tpl.php:648 +#: tpl/dash/dashboard.tpl.php:692 +#: tpl/dash/dashboard.tpl.php:736 +#: tpl/dash/dashboard.tpl.php:780 msgid "Force cron" msgstr "" -#: tpl/dash/dashboard.tpl.php:670 -#: tpl/toolbox/purge.tpl.php:71 +#: tpl/dash/dashboard.tpl.php:656 +#: tpl/dash/dashboard.tpl.php:700 +#: tpl/dash/dashboard.tpl.php:744 +#: tpl/dash/dashboard.tpl.php:788 +msgid "Last requested: %s" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:667 +#: tpl/toolbox/purge.tpl.php:82 msgid "Unique CSS" msgstr "" -#: tpl/dash/dashboard.tpl.php:744 +#: tpl/dash/dashboard.tpl.php:755 msgid "Viewport Image" msgstr "" -#: tpl/dash/dashboard.tpl.php:849 +#: tpl/dash/dashboard.tpl.php:863 msgid "Best available WordPress performance" msgstr "" -#: tpl/dash/dashboard.tpl.php:852 -msgid "Globally fast TTFB, easy setup, and more!" +#: tpl/dash/dashboard.tpl.php:868 +msgid "Globally fast TTFB, easy setup, and %s!" +msgstr "" + +#: tpl/dash/dashboard.tpl.php:869 +msgid "more" msgstr "" -#: tpl/dash/dashboard.tpl.php:866 +#: tpl/dash/dashboard.tpl.php:886 msgid "Refresh QUIC.cloud status" msgstr "" -#: tpl/dash/entry.tpl.php:12 +#: tpl/dash/entry.tpl.php:21 msgid "Network Dashboard" msgstr "" -#: tpl/dash/entry.tpl.php:21 +#: tpl/dash/entry.tpl.php:29 msgid "LiteSpeed Cache Dashboard" msgstr "" -#: tpl/dash/network_dash.tpl.php:20 -msgid "Usage Statistics" +#: tpl/dash/network_dash.tpl.php:28 +msgid "Usage Statistics: %s" msgstr "" -#: tpl/dash/network_dash.tpl.php:90 +#: tpl/dash/network_dash.tpl.php:107 msgid "Pay as You Go" msgstr "" -#: tpl/dash/network_dash.tpl.php:92 -msgid "This Month Usage" +#: tpl/dash/network_dash.tpl.php:109 +msgid "This Month Usage: %s" msgstr "" -#: tpl/db_optm/entry.tpl.php:11 -#: tpl/db_optm/settings.tpl.php:11 +#: tpl/db_optm/entry.tpl.php:17 +#: tpl/db_optm/settings.tpl.php:19 msgid "DB Optimization Settings" msgstr "" -#: tpl/db_optm/entry.tpl.php:18 +#: tpl/db_optm/entry.tpl.php:24 msgid "LiteSpeed Cache Database Optimization" msgstr "" -#: tpl/db_optm/manage.tpl.php:9 +#: tpl/db_optm/manage.tpl.php:17 msgid "Clean All" msgstr "" -#: tpl/db_optm/manage.tpl.php:13 +#: tpl/db_optm/manage.tpl.php:21 msgid "Post Revisions" msgstr "" -#: tpl/db_optm/manage.tpl.php:14 +#: tpl/db_optm/manage.tpl.php:22 msgid "Clean all post revisions" msgstr "" -#: tpl/db_optm/manage.tpl.php:17 +#: tpl/db_optm/manage.tpl.php:25 msgid "Orphaned Post Meta" msgstr "" -#: tpl/db_optm/manage.tpl.php:18 +#: tpl/db_optm/manage.tpl.php:26 msgid "Clean all orphaned post meta records" msgstr "" -#: tpl/db_optm/manage.tpl.php:21 +#: tpl/db_optm/manage.tpl.php:29 msgid "Auto Drafts" msgstr "" -#: tpl/db_optm/manage.tpl.php:22 +#: tpl/db_optm/manage.tpl.php:30 msgid "Clean all auto saved drafts" msgstr "" -#: tpl/db_optm/manage.tpl.php:25 +#: tpl/db_optm/manage.tpl.php:33 msgid "Trashed Posts" msgstr "" -#: tpl/db_optm/manage.tpl.php:26 +#: tpl/db_optm/manage.tpl.php:34 msgid "Clean all trashed posts and pages" msgstr "" -#: tpl/db_optm/manage.tpl.php:29 +#: tpl/db_optm/manage.tpl.php:37 msgid "Spam Comments" msgstr "" -#: tpl/db_optm/manage.tpl.php:30 +#: tpl/db_optm/manage.tpl.php:38 msgid "Clean all spam comments" msgstr "" -#: tpl/db_optm/manage.tpl.php:33 +#: tpl/db_optm/manage.tpl.php:41 msgid "Trashed Comments" msgstr "" -#: tpl/db_optm/manage.tpl.php:34 +#: tpl/db_optm/manage.tpl.php:42 msgid "Clean all trashed comments" msgstr "" -#: tpl/db_optm/manage.tpl.php:37 +#: tpl/db_optm/manage.tpl.php:45 msgid "Trackbacks/Pingbacks" msgstr "" -#: tpl/db_optm/manage.tpl.php:38 +#: tpl/db_optm/manage.tpl.php:46 msgid "Clean all trackbacks and pingbacks" msgstr "" -#: tpl/db_optm/manage.tpl.php:41 +#: tpl/db_optm/manage.tpl.php:49 msgid "Expired Transients" msgstr "" -#: tpl/db_optm/manage.tpl.php:42 +#: tpl/db_optm/manage.tpl.php:50 msgid "Clean expired transient options" msgstr "" -#: tpl/db_optm/manage.tpl.php:45 +#: tpl/db_optm/manage.tpl.php:53 msgid "All Transients" msgstr "" -#: tpl/db_optm/manage.tpl.php:46 +#: tpl/db_optm/manage.tpl.php:54 msgid "Clean all transient options" msgstr "" -#: tpl/db_optm/manage.tpl.php:49 +#: tpl/db_optm/manage.tpl.php:57 msgid "Optimize Tables" msgstr "" -#: tpl/db_optm/manage.tpl.php:50 +#: tpl/db_optm/manage.tpl.php:58 msgid "Optimize all tables in your database" msgstr "" -#: tpl/db_optm/manage.tpl.php:57 +#: tpl/db_optm/manage.tpl.php:66 msgid "Clean revisions older than %1$s day(s), excluding %2$s latest revisions" msgstr "" -#: tpl/db_optm/manage.tpl.php:78 +#: tpl/db_optm/manage.tpl.php:90 msgid "Database Optimizer" msgstr "" -#: tpl/db_optm/manage.tpl.php:110 +#: tpl/db_optm/manage.tpl.php:116 msgid "Database Table Engine Converter" msgstr "" -#: tpl/db_optm/manage.tpl.php:118 +#: tpl/db_optm/manage.tpl.php:124 msgid "Table" msgstr "" -#: tpl/db_optm/manage.tpl.php:119 +#: tpl/db_optm/manage.tpl.php:125 msgid "Engine" msgstr "" -#: tpl/db_optm/manage.tpl.php:120 +#: tpl/db_optm/manage.tpl.php:126 msgid "Tool" msgstr "" -#: tpl/db_optm/manage.tpl.php:135 +#: tpl/db_optm/manage.tpl.php:141 msgid "Convert to InnoDB" msgstr "" -#: tpl/db_optm/manage.tpl.php:143 +#: tpl/db_optm/manage.tpl.php:149 msgid "We are good. No table uses MyISAM engine." msgstr "" -#: tpl/db_optm/manage.tpl.php:165 +#: tpl/db_optm/manage.tpl.php:171 msgid "Database Summary" msgstr "" -#: tpl/db_optm/manage.tpl.php:181 +#: tpl/db_optm/manage.tpl.php:175 +msgid "Autoload size" +msgstr "" + +#: tpl/db_optm/manage.tpl.php:176 +msgid "Autoload entries" +msgstr "" + +#: tpl/db_optm/manage.tpl.php:180 +msgid "Autoload top list" +msgstr "" + +#: tpl/db_optm/manage.tpl.php:185 msgid "Option Name" msgstr "" -#: tpl/db_optm/manage.tpl.php:182 +#: tpl/db_optm/manage.tpl.php:186 msgid "Autoload" msgstr "" -#: tpl/db_optm/manage.tpl.php:183 +#: tpl/db_optm/manage.tpl.php:187 msgid "Size" msgstr "" -#: tpl/db_optm/settings.tpl.php:24 +#: tpl/db_optm/settings.tpl.php:32 msgid "Specify the number of most recent revisions to keep when cleaning revisions." msgstr "" -#: tpl/db_optm/settings.tpl.php:36 +#: tpl/db_optm/settings.tpl.php:44 msgid "Day(s)" msgstr "" -#: tpl/db_optm/settings.tpl.php:38 +#: tpl/db_optm/settings.tpl.php:46 msgid "Revisions newer than this many days will be kept when cleaning revisions." msgstr "" -#: tpl/esi_widget_edit.php:46 +#: tpl/esi_widget_edit.php:52 msgid "Public" msgstr "" -#: tpl/esi_widget_edit.php:47 +#: tpl/esi_widget_edit.php:53 msgid "Private" msgstr "" -#: tpl/esi_widget_edit.php:48 +#: tpl/esi_widget_edit.php:54 msgid "Disable" msgstr "" -#: tpl/esi_widget_edit.php:63 -msgid "Widget Cache TTL:" +#: tpl/esi_widget_edit.php:71 +msgid "Widget Cache TTL" msgstr "" -#: tpl/esi_widget_edit.php:73 +#: tpl/esi_widget_edit.php:81 msgid "Recommended value: 28800 seconds (8 hours)." msgstr "" -#: tpl/esi_widget_edit.php:74 +#: tpl/esi_widget_edit.php:82 msgid "A TTL of 0 indicates do not cache." msgstr "" -#: tpl/general/entry.tpl.php:8 -#: tpl/general/online.tpl.php:54 +#: tpl/general/entry.tpl.php:16 +#: tpl/general/online.tpl.php:68 msgid "Online Services" msgstr "" -#: tpl/general/entry.tpl.php:9 -#: tpl/general/entry.tpl.php:15 -#: tpl/general/network_settings.tpl.php:10 -#: tpl/general/settings.tpl.php:15 +#: tpl/general/entry.tpl.php:17 +#: tpl/general/entry.tpl.php:23 +#: tpl/general/network_settings.tpl.php:19 +#: tpl/general/settings.tpl.php:24 msgid "General Settings" msgstr "" -#: tpl/general/entry.tpl.php:10 -#: tpl/page_optm/entry.tpl.php:14 -#: tpl/page_optm/entry.tpl.php:15 +#: tpl/general/entry.tpl.php:18 +#: tpl/page_optm/entry.tpl.php:23 +#: tpl/page_optm/entry.tpl.php:24 msgid "Tuning" msgstr "" -#: tpl/general/entry.tpl.php:23 +#: tpl/general/entry.tpl.php:31 msgid "LiteSpeed Cache General Settings" msgstr "" -#: tpl/general/network_settings.tpl.php:22 +#: tpl/general/network_settings.tpl.php:31 msgid "Use Primary Site Configuration" msgstr "" -#: tpl/general/network_settings.tpl.php:26 +#: tpl/general/network_settings.tpl.php:35 msgid "Check this option to use the primary site's configuration for all subsites." msgstr "" -#: tpl/general/network_settings.tpl.php:27 +#: tpl/general/network_settings.tpl.php:36 msgid "This will disable the settings page on all subsites." msgstr "" -#: tpl/general/online.tpl.php:15 +#: tpl/general/online.tpl.php:22 msgid "QUIC.cloud Online Services" msgstr "" -#: tpl/general/online.tpl.php:23 +#: tpl/general/online.tpl.php:30 msgid "Current Cloud Nodes in Service" msgstr "" -#: tpl/general/online.tpl.php:24 +#: tpl/general/online.tpl.php:31 msgid "Click to clear all nodes for further redetection." msgstr "" -#: tpl/general/online.tpl.php:24 +#: tpl/general/online.tpl.php:31 msgid "Are you sure you want to clear all cloud nodes?" msgstr "" -#: tpl/general/online.tpl.php:36 +#: tpl/general/online.tpl.php:41 +msgid "Service:" +msgstr "" + +#: tpl/general/online.tpl.php:43 +msgid "Node:" +msgstr "" + +#: tpl/general/online.tpl.php:45 +msgid "Connected Date:" +msgstr "" + +#: tpl/general/online.tpl.php:51 msgid "No cloud services currently in use" msgstr "" -#: tpl/general/online.tpl.php:44 +#: tpl/general/online.tpl.php:59 msgid "QUIC.cloud Integration Disabled" msgstr "" -#: tpl/general/online.tpl.php:45 +#: tpl/general/online.tpl.php:60 msgid "Speed up your WordPress site even further with QUIC.cloud Online Services and CDN." msgstr "" -#: tpl/general/online.tpl.php:55 +#: tpl/general/online.tpl.php:69 msgid "QUIC.cloud's Online Services improve your site in the following ways:" msgstr "" -#: tpl/general/online.tpl.php:57 +#: tpl/general/online.tpl.php:71 msgid "Image Optimization gives you smaller image file sizes that transmit faster." msgstr "" -#: tpl/general/online.tpl.php:58 +#: tpl/general/online.tpl.php:72 msgid "Page Optimization streamlines page styles and visual elements for faster loading." msgstr "" -#: tpl/general/online.tpl.php:62 +#: tpl/general/online.tpl.php:76 msgid "QUIC.cloud's Image Optimization service does the following:" msgstr "" -#: tpl/general/online.tpl.php:64 +#: tpl/general/online.tpl.php:78 msgid "Processes your uploaded PNG and JPG images to produce smaller versions that don't sacrifice quality." msgstr "" -#: tpl/general/online.tpl.php:65 +#: tpl/general/online.tpl.php:79 msgid "Optionally creates next-generation WebP or AVIF image files." msgstr "" -#: tpl/general/online.tpl.php:67 +#: tpl/general/online.tpl.php:81 msgid "Processing for PNG, JPG, and WebP image formats is free. AVIF is available for a fee." msgstr "" -#: tpl/general/online.tpl.php:70 +#: tpl/general/online.tpl.php:84 msgid "QUIC.cloud's Page Optimization services address CSS bloat, and improve the user experience during page load, which can lead to improved page speed scores." msgstr "" -#: tpl/general/online.tpl.php:72 +#: tpl/general/online.tpl.php:86 msgid "Critical CSS (CCSS) loads visible above-the-fold content faster and with full styling." msgstr "" -#: tpl/general/online.tpl.php:73 +#: tpl/general/online.tpl.php:87 msgid "Unique CSS (UCSS) removes unused style definitions for a speedier page load overall." msgstr "" -#: tpl/general/online.tpl.php:74 +#: tpl/general/online.tpl.php:88 msgid "Low Quality Image Placeholder (LQIP) gives your imagery a more pleasing look as it lazy loads." msgstr "" -#: tpl/general/online.tpl.php:75 +#: tpl/general/online.tpl.php:89 msgid "Viewport Images (VPI) provides a well-polished fully-loaded view above the fold." msgstr "" -#: tpl/general/online.tpl.php:84 +#: tpl/general/online.tpl.php:98 msgid "Content Delivery Network" msgstr "" -#: tpl/general/online.tpl.php:88 +#: tpl/general/online.tpl.php:100 +msgid "QUIC.cloud CDN:" +msgstr "" + +#: tpl/general/online.tpl.php:102 msgid "Caches your entire site, including dynamic content and ESI blocks." msgstr "" -#: tpl/general/online.tpl.php:89 +#: tpl/general/online.tpl.php:103 msgid "Delivers global coverage with a growing network of 80+ PoPs." msgstr "" -#: tpl/general/online.tpl.php:90 +#: tpl/general/online.tpl.php:104 msgid "Provides security at the CDN level, protecting your server from attack." msgstr "" -#: tpl/general/online.tpl.php:91 +#: tpl/general/online.tpl.php:105 msgid "Offers optional built-in DNS service to simplify CDN onboarding." msgstr "" -#: tpl/general/online.tpl.php:100 +#: tpl/general/online.tpl.php:114 msgid "In order to use most QUIC.cloud services, you need quota. QUIC.cloud gives you free quota every month, but if you need more, you can purchase it." msgstr "" -#: tpl/general/online.tpl.php:112 +#: tpl/general/online.tpl.php:125 msgid "QUIC.cloud Integration Enabled" msgstr "" -#: tpl/general/online.tpl.php:113 +#: tpl/general/online.tpl.php:126 msgid "Your site is connected and ready to use QUIC.cloud Online Services." msgstr "" -#: tpl/general/online.tpl.php:130 +#: tpl/general/online.tpl.php:136 +msgid "CDN - Enabled" +msgstr "" + +#: tpl/general/online.tpl.php:138 +msgid "CDN - Disabled" +msgstr "" + +#: tpl/general/online.tpl.php:143 msgid "QUIC.cloud Integration Enabled with limitations" msgstr "" -#: tpl/general/online.tpl.php:131 +#: tpl/general/online.tpl.php:144 msgid "Your site is connected and using QUIC.cloud Online Services as an anonymous user. The CDN function and certain features of optimization services are not available for anonymous users. Link to QUIC.cloud to use the CDN and all available Online Services features." msgstr "" -#: tpl/general/online.tpl.php:137 -msgid "not available for anonymous users" +#: tpl/general/online.tpl.php:150 +msgid "CDN - not available for anonymous users" msgstr "" -#: tpl/general/online.tpl.php:147 +#: tpl/general/online.tpl.php:159 msgid "Are you sure you want to disconnect from QUIC.cloud? This will not remove any data from the QUIC.cloud dashboard." msgstr "" -#: tpl/general/online.tpl.php:147 +#: tpl/general/online.tpl.php:159 msgid "Disconnect from QUIC.cloud" msgstr "" -#: tpl/general/online.tpl.php:148 +#: tpl/general/online.tpl.php:160 msgid "Remove QUIC.cloud integration from this site. Note: QUIC.cloud data will be preserved so you can re-enable services at any time. If you want to fully remove your site from QUIC.cloud, delete the domain through the QUIC.cloud Dashboard first." msgstr "" -#: tpl/general/settings.tpl.php:39 +#: tpl/general/settings.tpl.php:48 msgid "This option enables maximum optimization for Guest Mode visitors." msgstr "" -#: tpl/general/settings.tpl.php:40 +#: tpl/general/settings.tpl.php:49 msgid "Please read all warnings before enabling this option." msgstr "" -#: tpl/general/settings.tpl.php:55 -msgid "Your %1$1s quota on %2$2s will still be in use." +#: tpl/general/settings.tpl.php:64 +msgid "Your %1$s quota on %2$s will still be in use." msgstr "" -#: tpl/general/settings.tpl.php:63 -#: tpl/general/settings.tpl.php:70 -#: tpl/general/settings.tpl.php:77 -#: tpl/general/settings.tpl.php:94 -#: tpl/page_optm/settings_media.tpl.php:244 -#: tpl/page_optm/settings_vpi.tpl.php:37 +#: tpl/general/settings.tpl.php:72 +#: tpl/general/settings.tpl.php:79 +#: tpl/general/settings.tpl.php:86 +#: tpl/general/settings.tpl.php:103 +#: tpl/page_optm/settings_media.tpl.php:253 +#: tpl/page_optm/settings_vpi.tpl.php:44 msgid "Notice" msgstr "" -#: tpl/general/settings.tpl.php:63 -#: tpl/page_optm/settings_media.tpl.php:244 -#: tpl/page_optm/settings_vpi.tpl.php:37 +#: tpl/general/settings.tpl.php:72 +#: tpl/page_optm/settings_media.tpl.php:253 +#: tpl/page_optm/settings_vpi.tpl.php:44 msgid "%s must be turned ON for this setting to work." msgstr "" -#: tpl/general/settings.tpl.php:70 +#: tpl/general/settings.tpl.php:79 msgid "You need to turn %s on to get maximum result." msgstr "" -#: tpl/general/settings.tpl.php:77 +#: tpl/general/settings.tpl.php:86 msgid "You need to turn %s on and finish all WebP generation to get maximum result." msgstr "" -#: tpl/general/settings.tpl.php:92 +#: tpl/general/settings.tpl.php:101 msgid "Enter this site's IP address to allow cloud services directly call IP instead of domain name. This eliminates the overhead of DNS and CDN lookups." msgstr "" -#: tpl/general/settings.tpl.php:93 +#: tpl/general/settings.tpl.php:102 msgid "Your server IP" msgstr "" -#: tpl/general/settings.tpl.php:93 +#: tpl/general/settings.tpl.php:102 msgid "Check my public IP from" msgstr "" -#: tpl/general/settings.tpl.php:94 +#: tpl/general/settings.tpl.php:103 msgid "the auto-detected IP may not be accurate if you have an additional outgoing IP set, or you have multiple IPs configured on your server." msgstr "" -#: tpl/general/settings.tpl.php:95 +#: tpl/general/settings.tpl.php:104 msgid "Please make sure this IP is the correct one for visiting your site." msgstr "" -#: tpl/general/settings.tpl.php:110 +#: tpl/general/settings.tpl.php:119 msgid "Turn this option ON to show latest news automatically, including hotfixes, new releases, available beta versions, and promotions." msgstr "" -#: tpl/general/settings_inc.auto_upgrade.tpl.php:16 +#: tpl/general/settings_inc.auto_upgrade.tpl.php:25 msgid "Turn this option ON to have LiteSpeed Cache updated automatically, whenever a new version is released. If OFF, update manually as usual." msgstr "" -#: tpl/general/settings_inc.guest.tpl.php:17 +#: tpl/general/settings_inc.guest.tpl.php:26 msgid "Guest Mode provides an always cacheable landing page for an automated guest's first time visit, and then attempts to update cache varies via AJAX." msgstr "" -#: tpl/general/settings_inc.guest.tpl.php:18 +#: tpl/general/settings_inc.guest.tpl.php:27 msgid "This option can help to correct the cache vary for certain advanced mobile or tablet visitors." msgstr "" -#: tpl/general/settings_inc.guest.tpl.php:25 +#: tpl/general/settings_inc.guest.tpl.php:34 msgid "Guest Mode testing result" msgstr "" -#: tpl/general/settings_inc.guest.tpl.php:26 +#: tpl/general/settings_inc.guest.tpl.php:35 msgid "Testing" msgstr "" -#: tpl/general/settings_inc.guest.tpl.php:33 +#: tpl/general/settings_inc.guest.tpl.php:42 msgid "Guest Mode passed testing." msgstr "" -#: tpl/general/settings_inc.guest.tpl.php:36 -#: tpl/general/settings_inc.guest.tpl.php:39 +#: tpl/general/settings_inc.guest.tpl.php:45 +#: tpl/general/settings_inc.guest.tpl.php:48 msgid "Guest Mode failed to test." msgstr "" -#: tpl/general/settings_tuning.tpl.php:9 -#: tpl/page_optm/settings_tuning.tpl.php:20 -#: tpl/page_optm/settings_tuning_css.tpl.php:8 +#: tpl/general/settings_tuning.tpl.php:19 +#: tpl/page_optm/settings_tuning.tpl.php:29 msgid "Tuning Settings" msgstr "" -#: tpl/general/settings_tuning.tpl.php:30 +#: tpl/general/settings_tuning.tpl.php:40 msgid "Listed User Agents will be considered as Guest Mode visitors." msgstr "" -#: tpl/general/settings_tuning.tpl.php:52 +#: tpl/general/settings_tuning.tpl.php:62 msgid "Listed IPs will be considered as Guest Mode visitors." msgstr "" -#: tpl/img_optm/entry.tpl.php:9 -#: tpl/img_optm/entry.tpl.php:15 -#: tpl/img_optm/network_settings.tpl.php:10 -#: tpl/img_optm/settings.tpl.php:11 +#: tpl/img_optm/entry.tpl.php:17 +#: tpl/img_optm/entry.tpl.php:22 +#: tpl/img_optm/network_settings.tpl.php:19 +#: tpl/img_optm/settings.tpl.php:19 msgid "Image Optimization Settings" msgstr "" -#: tpl/img_optm/entry.tpl.php:23 +#: tpl/img_optm/entry.tpl.php:30 msgid "LiteSpeed Cache Image Optimization" msgstr "" -#: tpl/img_optm/settings.media_webp.tpl.php:17 +#: tpl/img_optm/settings.media_webp.tpl.php:25 msgid "Request WebP/AVIF versions of original images when doing optimization." msgstr "" -#: tpl/img_optm/settings.media_webp.tpl.php:18 +#: tpl/img_optm/settings.media_webp.tpl.php:26 msgid "Significantly improve load time by replacing images with their optimized %s versions." msgstr "" -#: tpl/img_optm/settings.media_webp.tpl.php:23 +#: tpl/img_optm/settings.media_webp.tpl.php:31 msgid "%1$s is a %2$s paid feature." msgstr "" -#: tpl/img_optm/settings.media_webp.tpl.php:26 +#: tpl/img_optm/settings.media_webp.tpl.php:34 msgid "When switching formats, please %1$s or %2$s to apply this new choice to previously optimized images." msgstr "" -#: tpl/img_optm/settings.media_webp.tpl.php:26 -#: tpl/img_optm/summary.tpl.php:361 +#: tpl/img_optm/settings.media_webp.tpl.php:34 +#: tpl/img_optm/summary.tpl.php:378 msgid "Destroy All Optimization Data" msgstr "" -#: tpl/img_optm/settings.media_webp.tpl.php:26 -#: tpl/img_optm/summary.tpl.php:352 +#: tpl/img_optm/settings.media_webp.tpl.php:34 +#: tpl/img_optm/summary.tpl.php:368 msgid "Soft Reset Optimization Counter" msgstr "" -#: tpl/img_optm/settings.tpl.php:26 +#: tpl/img_optm/settings.tpl.php:34 msgid "Automatically request optimization via cron job." msgstr "" -#: tpl/img_optm/settings.tpl.php:39 +#: tpl/img_optm/settings.tpl.php:47 msgid "Optimize images and save backups of the originals in the same folder." msgstr "" -#: tpl/img_optm/settings.tpl.php:52 +#: tpl/img_optm/settings.tpl.php:60 msgid "Automatically remove the original image backups after fetching optimized images." msgstr "" -#: tpl/img_optm/settings.tpl.php:57 -#: tpl/img_optm/summary.tpl.php:235 +#: tpl/img_optm/settings.tpl.php:65 +#: tpl/img_optm/summary.tpl.php:244 +#: tpl/page_optm/settings_media.tpl.php:308 msgid "This is irreversible." msgstr "" -#: tpl/img_optm/settings.tpl.php:58 -#: tpl/img_optm/summary.tpl.php:236 +#: tpl/img_optm/settings.tpl.php:66 +#: tpl/img_optm/summary.tpl.php:245 msgid "You will be unable to Revert Optimization once the backups are deleted!" msgstr "" -#: tpl/img_optm/settings.tpl.php:72 +#: tpl/img_optm/settings.tpl.php:80 msgid "Optimize images using lossless compression." msgstr "" -#: tpl/img_optm/settings.tpl.php:73 +#: tpl/img_optm/settings.tpl.php:81 msgid "This can improve quality but may result in larger images than lossy compression will." msgstr "" -#: tpl/img_optm/settings.tpl.php:86 +#: tpl/img_optm/settings.tpl.php:104 +msgid "No sizes found." +msgstr "" + +#: tpl/img_optm/settings.tpl.php:107 +msgid "Choose which image sizes to optimize." +msgstr "" + +#: tpl/img_optm/settings.tpl.php:120 msgid "Preserve EXIF data (copyright, GPS, comments, keywords, etc) when optimizing." msgstr "" -#: tpl/img_optm/settings.tpl.php:87 +#: tpl/img_optm/settings.tpl.php:121 msgid "This will increase the size of optimized files." msgstr "" -#: tpl/img_optm/settings.tpl.php:117 +#: tpl/img_optm/settings.tpl.php:149 msgid "Specify which element attributes will be replaced with WebP/AVIF." msgstr "" -#: tpl/img_optm/settings.tpl.php:133 +#: tpl/img_optm/settings.tpl.php:165 msgid "Enable replacement of WebP/AVIF in %s elements that were generated outside of WordPress logic." msgstr "" -#: tpl/img_optm/summary.tpl.php:46 -#: tpl/page_optm/settings_css.tpl.php:106 -#: tpl/page_optm/settings_css.tpl.php:250 -#: tpl/page_optm/settings_media.tpl.php:184 -#: tpl/page_optm/settings_vpi.tpl.php:53 -msgid "Current closest Cloud server is %s. Click to redetect." -msgstr "" - -#: tpl/img_optm/summary.tpl.php:50 +#: tpl/img_optm/summary.tpl.php:58 msgid "Optimize images with our QUIC.cloud server" msgstr "" -#: tpl/img_optm/summary.tpl.php:55 +#: tpl/img_optm/summary.tpl.php:63 msgid "You can request a maximum of %s images at once." msgstr "" -#: tpl/img_optm/summary.tpl.php:60 +#: tpl/img_optm/summary.tpl.php:68 msgid "To make sure our server can communicate with your server without any issues and everything works fine, for the few first requests the number of image groups allowed in a single request is limited." msgstr "" -#: tpl/img_optm/summary.tpl.php:61 +#: tpl/img_optm/summary.tpl.php:69 msgid "Current limit is" msgstr "" -#: tpl/img_optm/summary.tpl.php:69 +#: tpl/img_optm/summary.tpl.php:77 #: tpl/page_optm/settings_css.tpl.php:156 -#: tpl/page_optm/settings_css.tpl.php:300 -#: tpl/page_optm/settings_vpi.tpl.php:99 +#: tpl/page_optm/settings_css.tpl.php:293 +#: tpl/page_optm/settings_vpi.tpl.php:101 msgid "Available after %d second(s)" msgstr "" -#: tpl/img_optm/summary.tpl.php:85 +#: tpl/img_optm/summary.tpl.php:93 msgid "Only press the button if the pull cron job is disabled." msgstr "" -#: tpl/img_optm/summary.tpl.php:85 +#: tpl/img_optm/summary.tpl.php:93 msgid "Images will be pulled automatically if the cron job is running." msgstr "" -#: tpl/img_optm/summary.tpl.php:94 +#: tpl/img_optm/summary.tpl.php:102 msgid "Pull Images" msgstr "" -#: tpl/img_optm/summary.tpl.php:100 +#: tpl/img_optm/summary.tpl.php:108 msgid "Optimization Status" msgstr "" -#: tpl/img_optm/summary.tpl.php:133 +#: tpl/img_optm/summary.tpl.php:141 msgid "After the QUIC.cloud Image Optimization server finishes optimization, it will notify your site to pull the optimized images." msgstr "" -#: tpl/img_optm/summary.tpl.php:134 +#: tpl/img_optm/summary.tpl.php:142 msgid "This process is automatic." msgstr "" -#: tpl/img_optm/summary.tpl.php:149 +#: tpl/img_optm/summary.tpl.php:156 msgid "Last pull initiated by cron at %s." msgstr "" -#: tpl/img_optm/summary.tpl.php:177 +#: tpl/img_optm/summary.tpl.php:184 msgid "Storage Optimization" msgstr "" -#: tpl/img_optm/summary.tpl.php:181 +#: tpl/img_optm/summary.tpl.php:188 msgid "A backup of each image is saved before it is optimized." msgstr "" -#: tpl/img_optm/summary.tpl.php:188 +#: tpl/img_optm/summary.tpl.php:194 msgid "Last calculated" msgstr "" -#: tpl/img_optm/summary.tpl.php:192 -#: tpl/img_optm/summary.tpl.php:247 +#: tpl/img_optm/summary.tpl.php:198 +#: tpl/img_optm/summary.tpl.php:256 msgid "Files" msgstr "" -#: tpl/img_optm/summary.tpl.php:203 +#: tpl/img_optm/summary.tpl.php:208 msgid "Calculate Original Image Storage" msgstr "" -#: tpl/img_optm/summary.tpl.php:212 +#: tpl/img_optm/summary.tpl.php:217 msgid "Calculate Backups Disk Space" msgstr "" -#: tpl/img_optm/summary.tpl.php:219 +#: tpl/img_optm/summary.tpl.php:224 msgid "Image Thumbnail Group Sizes" msgstr "" -#: tpl/img_optm/summary.tpl.php:232 +#: tpl/img_optm/summary.tpl.php:241 msgid "Delete all backups of the original images" msgstr "" -#: tpl/img_optm/summary.tpl.php:244 -#: tpl/page_optm/settings_localization.tpl.php:52 +#: tpl/img_optm/summary.tpl.php:253 +#: tpl/page_optm/settings_localization.tpl.php:61 msgid "Last ran" msgstr "" -#: tpl/img_optm/summary.tpl.php:250 +#: tpl/img_optm/summary.tpl.php:259 msgid "Saved" msgstr "" -#: tpl/img_optm/summary.tpl.php:254 +#: tpl/img_optm/summary.tpl.php:264 msgid "Are you sure you want to remove all image backups?" msgstr "" -#: tpl/img_optm/summary.tpl.php:255 +#: tpl/img_optm/summary.tpl.php:265 msgid "Remove Original Image Backups" msgstr "" -#: tpl/img_optm/summary.tpl.php:266 +#: tpl/img_optm/summary.tpl.php:276 msgid "Image Information" msgstr "" -#: tpl/img_optm/summary.tpl.php:275 +#: tpl/img_optm/summary.tpl.php:285 msgid "Image groups total" msgstr "" -#: tpl/img_optm/summary.tpl.php:280 +#: tpl/img_optm/summary.tpl.php:289 msgid "Congratulations, all gathered!" msgstr "" -#: tpl/img_optm/summary.tpl.php:283 +#: tpl/img_optm/summary.tpl.php:291 msgid "What is a group?" msgstr "" -#: tpl/img_optm/summary.tpl.php:285 +#: tpl/img_optm/summary.tpl.php:293 msgid "What is an image group?" msgstr "" -#: tpl/img_optm/summary.tpl.php:289 -#: tpl/img_optm/summary.tpl.php:356 +#: tpl/img_optm/summary.tpl.php:297 +#: tpl/img_optm/summary.tpl.php:372 msgid "Current image post id position" msgstr "" -#: tpl/img_optm/summary.tpl.php:290 +#: tpl/img_optm/summary.tpl.php:298 msgid "Maximum image post id" msgstr "" -#: tpl/img_optm/summary.tpl.php:296 +#: tpl/img_optm/summary.tpl.php:304 msgid "Scan for any new unoptimized image thumbnail sizes and resend necessary image optimization requests." msgstr "" -#: tpl/img_optm/summary.tpl.php:297 +#: tpl/img_optm/summary.tpl.php:305 msgid "Rescan New Thumbnails" msgstr "" -#: tpl/img_optm/summary.tpl.php:305 +#: tpl/img_optm/summary.tpl.php:313 msgid "Optimization Summary" msgstr "" -#: tpl/img_optm/summary.tpl.php:317 +#: tpl/img_optm/summary.tpl.php:325 msgid "Last Pulled" msgstr "" -#: tpl/img_optm/summary.tpl.php:325 -msgid "Results can be checked in Media Library." +#. translators: %s: Link tags +#: tpl/img_optm/summary.tpl.php:337 +msgid "Results can be checked in %sMedia Library%s." msgstr "" -#: tpl/img_optm/summary.tpl.php:331 +#: tpl/img_optm/summary.tpl.php:347 msgid "Optimization Tools" msgstr "" -#: tpl/img_optm/summary.tpl.php:334 +#: tpl/img_optm/summary.tpl.php:350 msgid "You can quickly switch between using original (unoptimized versions) and optimized image files. It will affect all images on your website, both regular and webp versions if available." msgstr "" -#: tpl/img_optm/summary.tpl.php:339 +#: tpl/img_optm/summary.tpl.php:355 msgid "Use original images (unoptimized) on your site" msgstr "" -#: tpl/img_optm/summary.tpl.php:340 +#: tpl/img_optm/summary.tpl.php:356 msgid "Use Original Files" msgstr "" -#: tpl/img_optm/summary.tpl.php:343 +#: tpl/img_optm/summary.tpl.php:359 msgid "Switch back to using optimized images on your site" msgstr "" -#: tpl/img_optm/summary.tpl.php:344 +#: tpl/img_optm/summary.tpl.php:360 msgid "Use Optimized Files" msgstr "" -#: tpl/img_optm/summary.tpl.php:356 +#: tpl/img_optm/summary.tpl.php:372 msgid "This will reset the %1$s. If you changed WebP/AVIF settings and want to generate %2$s for the previously optimized images, use this action." msgstr "" -#: tpl/img_optm/summary.tpl.php:360 +#: tpl/img_optm/summary.tpl.php:377 msgid "Are you sure to destroy all optimized images?" msgstr "" -#: tpl/img_optm/summary.tpl.php:365 +#: tpl/img_optm/summary.tpl.php:382 msgid "Remove all previous image optimization requests/results, revert completed optimizations, and delete all optimization files." msgstr "" -#: tpl/inc/admin_footer.php:11 +#: tpl/inc/admin_footer.php:17 msgid "Rate %1$s on %2$s" msgstr "" -#: tpl/inc/admin_footer.php:14 +#: tpl/inc/admin_footer.php:19 msgid "Read LiteSpeed Documentation" msgstr "" -#: tpl/inc/admin_footer.php:16 +#: tpl/inc/admin_footer.php:21 msgid "Visit LSCWP support forum" msgstr "" -#: tpl/inc/admin_footer.php:18 +#: tpl/inc/admin_footer.php:23 msgid "Join LiteSpeed Slack community" msgstr "" -#: tpl/inc/check_cache_disabled.php:11 +#: tpl/inc/check_cache_disabled.php:20 msgid "To use the caching functions you must have a LiteSpeed web server or be using QUIC.cloud CDN." msgstr "" -#: tpl/inc/check_cache_disabled.php:16 +#: tpl/inc/check_cache_disabled.php:25 msgid "Please enable the LSCache Module at the server level, or ask your hosting provider." msgstr "" -#: tpl/inc/check_cache_disabled.php:22 +#: tpl/inc/check_cache_disabled.php:31 msgid "Please enable LiteSpeed Cache in the plugin settings." msgstr "" -#: tpl/inc/check_cache_disabled.php:34 +#: tpl/inc/check_cache_disabled.php:40 msgid "LSCache caching functions on this page are currently unavailable!" msgstr "" -#: tpl/inc/check_if_network_disable_all.php:21 +#: tpl/inc/check_if_network_disable_all.php:30 msgid "The network admin selected use primary site configs for all subsites." msgstr "" -#: tpl/inc/check_if_network_disable_all.php:22 +#: tpl/inc/check_if_network_disable_all.php:31 msgid "The following options are selected, but are not editable in this settings page." msgstr "" -#: tpl/inc/in_upgrading.php:6 +#: tpl/inc/in_upgrading.php:15 msgid "LiteSpeed cache plugin upgraded. Please refresh the page to complete the configuration data upgrade." msgstr "" -#: tpl/inc/show_display_installed.php:8 +#: tpl/inc/modal.deactivation.php:22 +msgid "The deactivation is temporary" +msgstr "" + +#: tpl/inc/modal.deactivation.php:28 +msgid "Site performance is worse" +msgstr "" + +#: tpl/inc/modal.deactivation.php:33 +msgid "Plugin is too complicated" +msgstr "" + +#: tpl/inc/modal.deactivation.php:38 +msgid "Other" +msgstr "" + +#: tpl/inc/modal.deactivation.php:47 +msgid "Why are you deactivating the plugin?" +msgstr "" + +#: tpl/inc/modal.deactivation.php:60 +msgid "On uninstall, all plugin settings will be deleted." +msgstr "" + +#: tpl/inc/modal.deactivation.php:68 +msgid "If you have used Image Optimization, please %sDestroy All Optimization Data%s first. NOTE: this does not remove your optimized images." +msgstr "" + +#: tpl/inc/modal.deactivation.php:76 +msgid "Deactivate" +msgstr "" + +#: tpl/inc/modal.deactivation.php:76 +msgid "Deactivate plugin" +msgstr "" + +#: tpl/inc/modal.deactivation.php:77 +msgid "Close popup" +msgstr "" + +#: tpl/inc/show_display_installed.php:26 msgid "LiteSpeed Cache plugin is installed!" msgstr "" -#: tpl/inc/show_display_installed.php:11 +#: tpl/inc/show_display_installed.php:27 msgid "This message indicates that the plugin was installed by the server admin." msgstr "" -#: tpl/inc/show_display_installed.php:13 +#: tpl/inc/show_display_installed.php:28 msgid "The LiteSpeed Cache plugin is used to cache pages - a simple way to improve the performance of the site." msgstr "" -#: tpl/inc/show_display_installed.php:15 +#: tpl/inc/show_display_installed.php:29 msgid "However, there is no way of knowing all the possible customizations that were implemented." msgstr "" -#: tpl/inc/show_display_installed.php:17 +#: tpl/inc/show_display_installed.php:30 msgid "For that reason, please test the site to make sure everything still functions properly." msgstr "" -#: tpl/inc/show_display_installed.php:19 +#: tpl/inc/show_display_installed.php:31 msgid "Examples of test cases include:" msgstr "" -#: tpl/inc/show_display_installed.php:22 +#: tpl/inc/show_display_installed.php:32 msgid "Visit the site while logged out." msgstr "" -#: tpl/inc/show_display_installed.php:25 +#: tpl/inc/show_display_installed.php:33 msgid "Create a post, make sure the front page is accurate." msgstr "" -#: tpl/inc/show_display_installed.php:29 -msgid "If there are any questions, the team is always happy to answer any questions on the support forum." +#. translators: %s: Link tags +#: tpl/inc/show_display_installed.php:37 +msgid "If there are any questions, the team is always happy to answer any questions on the %ssupport forum%s." msgstr "" -#: tpl/inc/show_display_installed.php:33 +#: tpl/inc/show_display_installed.php:41 msgid "If you would rather not move at litespeed, you can deactivate this plugin." msgstr "" -#: tpl/inc/show_error_cookie.php:7 +#: tpl/inc/show_error_cookie.php:16 msgid "NOTICE: Database login cookie did not match your login cookie." msgstr "" -#: tpl/inc/show_error_cookie.php:9 +#: tpl/inc/show_error_cookie.php:18 msgid "If the login cookie was recently changed in the settings, please log out and back in." msgstr "" -#: tpl/inc/show_error_cookie.php:11 -msgid "If not, please verify the setting in the Advanced tab." +#: tpl/inc/show_error_cookie.php:21 +msgid "If not, please verify the setting in the %sAdvanced tab%s." msgstr "" -#: tpl/inc/show_error_cookie.php:14 +#: tpl/inc/show_error_cookie.php:27 msgid "If using OpenLiteSpeed, the server must be restarted once for the changes to take effect." msgstr "" -#: tpl/inc/show_rule_conflict.php:7 -msgid "Unexpected cache rule %2$s found in %1$s file. This rule may cause visitors to see old versions of pages due to the browser caching HTML pages. If you are sure that HTML pages are not being browser cached, this message can be dismissed. (Learn More)" +#: tpl/inc/show_rule_conflict.php:16 +msgid "Unexpected cache rule %2$s found in %1$s file. This rule may cause visitors to see old versions of pages due to the browser caching HTML pages. If you are sure that HTML pages are not being browser cached, this message can be dismissed. (%3$sLearn More%4$s)" msgstr "" -#: tpl/page_optm/entry.tpl.php:7 -#: tpl/page_optm/settings_css.tpl.php:25 +#: tpl/optimax/entry.tpl.php:16 +msgid "OptimaX Summary" +msgstr "" + +#: tpl/optimax/entry.tpl.php:17 +#: tpl/optimax/entry.tpl.php:22 +#: tpl/optimax/settings.tpl.php:19 +msgid "OptimaX Settings" +msgstr "" + +#: tpl/optimax/entry.tpl.php:30 +msgid "LiteSpeed Cache OptimaX" +msgstr "" + +#: tpl/optimax/settings.tpl.php:34 +msgid "Turn on OptimaX. This will automatically request your pages OptimaX result via cron job." +msgstr "" + +#: tpl/page_optm/entry.tpl.php:16 +#: tpl/page_optm/settings_css.tpl.php:31 msgid "CSS Settings" msgstr "" -#: tpl/page_optm/entry.tpl.php:8 -#: tpl/page_optm/settings_js.tpl.php:9 +#: tpl/page_optm/entry.tpl.php:17 +#: tpl/page_optm/settings_js.tpl.php:17 msgid "JS Settings" msgstr "" -#: tpl/page_optm/entry.tpl.php:9 -#: tpl/page_optm/settings_html.tpl.php:9 +#: tpl/page_optm/entry.tpl.php:18 +#: tpl/page_optm/settings_html.tpl.php:17 msgid "HTML Settings" msgstr "" -#: tpl/page_optm/entry.tpl.php:10 -#: tpl/page_optm/settings_media.tpl.php:16 +#: tpl/page_optm/entry.tpl.php:19 +#: tpl/page_optm/settings_media.tpl.php:26 msgid "Media Settings" msgstr "" -#: tpl/page_optm/entry.tpl.php:11 +#: tpl/page_optm/entry.tpl.php:20 msgid "VPI" msgstr "" -#: tpl/page_optm/entry.tpl.php:12 -#: tpl/page_optm/settings_media_exc.tpl.php:8 +#: tpl/page_optm/entry.tpl.php:21 +#: tpl/page_optm/settings_media_exc.tpl.php:17 msgid "Media Excludes" msgstr "" -#: tpl/page_optm/entry.tpl.php:13 +#: tpl/page_optm/entry.tpl.php:22 msgid "Localization" msgstr "" -#: tpl/page_optm/entry.tpl.php:22 +#: tpl/page_optm/entry.tpl.php:31 msgid "LiteSpeed Cache Page Optimization" msgstr "" -#: tpl/page_optm/entry.tpl.php:34 +#: tpl/page_optm/entry.tpl.php:43 msgid "Please test thoroughly when enabling any option in this list. After changing Minify/Combine settings, please do a Purge All action." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:41 +#: tpl/page_optm/settings_css.tpl.php:46 msgid "Minify CSS files and inline CSS code." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:55 +#: tpl/page_optm/settings_css.tpl.php:60 msgid "Combine CSS files and inline CSS code." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:56 -#: tpl/page_optm/settings_js.tpl.php:40 +#: tpl/page_optm/settings_css.tpl.php:61 +#: tpl/page_optm/settings_js.tpl.php:48 msgid "How to Fix Problems Caused by CSS/JS Optimization." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:77 +#: tpl/page_optm/settings_css.tpl.php:82 msgid "Use QUIC.cloud online service to generate unique CSS." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:78 +#: tpl/page_optm/settings_css.tpl.php:83 msgid "This will drop the unused CSS on each page from the combined file." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:80 +#: tpl/page_optm/settings_css.tpl.php:85 msgid "Automatic generation of unique CSS is in the background via a cron-based queue." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:82 +#: tpl/page_optm/settings_css.tpl.php:87 msgid "Filter %s available for UCSS per page type generation." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:87 +#: tpl/page_optm/settings_css.tpl.php:93 msgid "This option is bypassed because %1$s option is %2$s." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:100 -#: tpl/page_optm/settings_css.tpl.php:244 +#: tpl/page_optm/settings_css.tpl.php:102 +#: tpl/page_optm/settings_css.tpl.php:239 +#: tpl/page_optm/settings_media.tpl.php:188 +#: tpl/page_optm/settings_vpi.tpl.php:53 +msgid "Last generated" +msgstr "" + +#: tpl/page_optm/settings_css.tpl.php:105 +#: tpl/page_optm/settings_css.tpl.php:242 msgid "Last requested cost" msgstr "" -#: tpl/page_optm/settings_css.tpl.php:112 -#: tpl/page_optm/settings_css.tpl.php:256 -#: tpl/page_optm/settings_vpi.tpl.php:59 +#: tpl/page_optm/settings_css.tpl.php:117 +#: tpl/page_optm/settings_css.tpl.php:254 +#: tpl/page_optm/settings_vpi.tpl.php:65 msgid "URL list in %s queue waiting for cron" msgstr "" +#: tpl/page_optm/settings_css.tpl.php:118 +#: tpl/page_optm/settings_css.tpl.php:255 +#: tpl/page_optm/settings_media.tpl.php:201 +#: tpl/page_optm/settings_vpi.tpl.php:66 +msgid "Clear" +msgstr "" + #: tpl/page_optm/settings_css.tpl.php:155 #: tpl/page_optm/settings_css.tpl.php:160 -#: tpl/page_optm/settings_css.tpl.php:299 -#: tpl/page_optm/settings_css.tpl.php:304 -#: tpl/page_optm/settings_vpi.tpl.php:98 -#: tpl/page_optm/settings_vpi.tpl.php:103 +#: tpl/page_optm/settings_css.tpl.php:292 +#: tpl/page_optm/settings_css.tpl.php:297 +#: tpl/page_optm/settings_vpi.tpl.php:100 +#: tpl/page_optm/settings_vpi.tpl.php:105 msgid "Run %s Queue Manually" msgstr "" -#: tpl/page_optm/settings_css.tpl.php:179 +#: tpl/page_optm/settings_css.tpl.php:178 msgid "Inline UCSS to reduce the extra CSS file loading. This option will not be automatically turned on for %1$s pages. To use it on %1$s pages, please set it to ON." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:182 +#: tpl/page_optm/settings_css.tpl.php:181 msgid "This option will automatically bypass %s option." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:196 +#: tpl/page_optm/settings_css.tpl.php:195 msgid "Include external CSS and inline CSS in combined file when %1$s is also enabled. This option helps maintain the priorities of CSS, which should minimize potential errors caused by CSS Combine." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:216 +#: tpl/page_optm/settings_css.tpl.php:215 msgid "Optimize CSS delivery." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:217 -#: tpl/page_optm/settings_html.tpl.php:167 -#: tpl/page_optm/settings_js.tpl.php:73 +#: tpl/page_optm/settings_css.tpl.php:216 +#: tpl/page_optm/settings_html.tpl.php:175 +#: tpl/page_optm/settings_js.tpl.php:81 msgid "This can improve your speed score in services like Pingdom, GTmetrix and PageSpeed." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:218 +#: tpl/page_optm/settings_css.tpl.php:217 msgid "Use QUIC.cloud online service to generate critical CSS and load remaining CSS asynchronously." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:220 +#: tpl/page_optm/settings_css.tpl.php:219 msgid "Automatic generation of critical CSS is in the background via a cron-based queue." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:221 +#: tpl/page_optm/settings_css.tpl.php:220 msgid "When this option is turned %s, it will also load Google Fonts asynchronously." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:225 +#: tpl/page_optm/settings_css.tpl.php:224 msgid "Elements with attribute %s in HTML code will be excluded." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:231 +#: tpl/page_optm/settings_css.tpl.php:230 msgid "This option is bypassed due to %s option." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:321 +#: tpl/page_optm/settings_css.tpl.php:314 msgid "Disable this option to generate CCSS per Post Type instead of per page. This can save significant CCSS quota, however it may result in incorrect CSS styling if your site uses a page builder." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:334 +#: tpl/page_optm/settings_css.tpl.php:327 msgid "This will inline the asynchronous CSS library to avoid render blocking." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:345 +#: tpl/page_optm/settings_css.tpl.php:338 msgid "Default" msgstr "" -#: tpl/page_optm/settings_css.tpl.php:347 +#: tpl/page_optm/settings_css.tpl.php:340 msgid "Set this to append %1$s to all %2$s rules before caching CSS to specify how fonts should be displayed while being downloaded." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:348 +#: tpl/page_optm/settings_css.tpl.php:341 msgid "%s is recommended." msgstr "" -#: tpl/page_optm/settings_css.tpl.php:348 +#: tpl/page_optm/settings_css.tpl.php:341 msgid "Swap" msgstr "" -#: tpl/page_optm/settings_html.tpl.php:23 +#: tpl/page_optm/settings_html.tpl.php:31 msgid "Minify HTML content." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:36 +#: tpl/page_optm/settings_html.tpl.php:44 msgid "Prefetching DNS can reduce latency for visitors." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:37 -#: tpl/page_optm/settings_html.tpl.php:68 +#: tpl/page_optm/settings_html.tpl.php:45 +#: tpl/page_optm/settings_html.tpl.php:76 msgid "For example" msgstr "" -#: tpl/page_optm/settings_html.tpl.php:52 +#: tpl/page_optm/settings_html.tpl.php:60 msgid "Automatically enable DNS prefetching for all URLs in the document, including images, CSS, JavaScript, and so forth." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:53 +#: tpl/page_optm/settings_html.tpl.php:61 msgid "This can improve the page loading speed." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:67 +#: tpl/page_optm/settings_html.tpl.php:75 msgid "Preconnecting speeds up future loads from a given origin." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:83 +#: tpl/page_optm/settings_html.tpl.php:91 msgid "Delay rendering off-screen HTML elements by its selector." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:98 +#: tpl/page_optm/settings_html.tpl.php:106 msgid "When minifying HTML do not discard comments that match a specified pattern." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:100 +#: tpl/page_optm/settings_html.tpl.php:108 msgid "If comment to be kept is like: %1$s write: %2$s" msgstr "" -#: tpl/page_optm/settings_html.tpl.php:115 +#: tpl/page_optm/settings_html.tpl.php:123 msgid "Remove query strings from internal static resources." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:119 +#: tpl/page_optm/settings_html.tpl.php:127 msgid "Google reCAPTCHA will be bypassed automatically." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:124 +#: tpl/page_optm/settings_html.tpl.php:132 msgid "Append query string %s to the resources to bypass this action." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:138 +#: tpl/page_optm/settings_html.tpl.php:146 msgid "Use Web Font Loader library to load Google Fonts asynchronously while leaving other CSS intact." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:139 +#: tpl/page_optm/settings_html.tpl.php:147 msgid "This will also add a preconnect to Google Fonts to establish a connection earlier." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:153 +#: tpl/page_optm/settings_html.tpl.php:161 msgid "Prevent Google Fonts from loading on all pages." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:166 +#: tpl/page_optm/settings_html.tpl.php:174 msgid "Stop loading WordPress.org emoji. Browser default emoji will be displayed instead." msgstr "" -#: tpl/page_optm/settings_html.tpl.php:180 +#: tpl/page_optm/settings_html.tpl.php:188 msgid "This option will remove all %s tags from HTML." msgstr "" -#: tpl/page_optm/settings_js.tpl.php:25 +#: tpl/page_optm/settings_js.tpl.php:33 msgid "Minify JS files and inline JS codes." msgstr "" -#: tpl/page_optm/settings_js.tpl.php:39 +#: tpl/page_optm/settings_js.tpl.php:47 msgid "Combine all local JS files into a single file." msgstr "" -#: tpl/page_optm/settings_js.tpl.php:43 -#: tpl/page_optm/settings_js.tpl.php:77 +#: tpl/page_optm/settings_js.tpl.php:51 +#: tpl/page_optm/settings_js.tpl.php:85 msgid "This option may result in a JS error or layout issue on frontend pages with certain themes/plugins." msgstr "" -#: tpl/page_optm/settings_js.tpl.php:44 +#: tpl/page_optm/settings_js.tpl.php:52 msgid "JS error can be found from the developer console of browser by right clicking and choosing Inspect." msgstr "" -#: tpl/page_optm/settings_js.tpl.php:58 +#: tpl/page_optm/settings_js.tpl.php:66 msgid "Include external JS and inline JS in combined file when %1$s is also enabled. This option helps maintain the priorities of JS execution, which should minimize potential errors caused by JS Combine." msgstr "" -#: tpl/page_optm/settings_js.tpl.php:69 +#: tpl/page_optm/settings_js.tpl.php:77 msgid "Deferred" msgstr "" -#: tpl/page_optm/settings_js.tpl.php:69 +#: tpl/page_optm/settings_js.tpl.php:77 msgid "Delayed" msgstr "" -#: tpl/page_optm/settings_js.tpl.php:71 +#: tpl/page_optm/settings_js.tpl.php:79 msgid "Deferring until page is parsed or delaying till interaction can help reduce resource contention and improve performance causing a lower FID (Core Web Vitals metric)." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:13 +#: tpl/page_optm/settings_localization.tpl.php:22 msgid "Failed to create Avatar table. Please follow Table Creation guidance from LiteSpeed Wiki to finish setup." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:18 +#: tpl/page_optm/settings_localization.tpl.php:27 msgid "Localization Settings" msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:31 +#: tpl/page_optm/settings_localization.tpl.php:40 msgid "Store Gravatar locally." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:32 +#: tpl/page_optm/settings_localization.tpl.php:41 msgid "Accelerates the speed by caching Gravatar (Globally Recognized Avatars)." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:45 +#: tpl/page_optm/settings_localization.tpl.php:54 msgid "Refresh Gravatar cache by cron." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:58 +#: tpl/page_optm/settings_localization.tpl.php:67 msgid "Avatar list in queue waiting for update" msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:63 -#: tpl/page_optm/settings_media.tpl.php:209 +#: tpl/page_optm/settings_localization.tpl.php:72 +#: tpl/page_optm/settings_media.tpl.php:218 msgid "Run Queue Manually" msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:80 +#: tpl/page_optm/settings_localization.tpl.php:89 msgid "Specify how long, in seconds, Gravatar files are cached." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:95 +#: tpl/page_optm/settings_localization.tpl.php:104 msgid "Localize external resources." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:99 +#: tpl/page_optm/settings_localization.tpl.php:108 msgid "Please thoroughly test all items in %s to ensure they function as expected." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:121 +#: tpl/page_optm/settings_localization.tpl.php:130 msgid "Resources listed here will be copied and replaced with local URLs." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:122 +#: tpl/page_optm/settings_localization.tpl.php:131 msgid "HTTPS sources only." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:126 +#: tpl/page_optm/settings_localization.tpl.php:135 msgid "Comments are supported. Start a line with a %s to turn it into a comment line." msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:128 -#: tpl/toolbox/beta_test.tpl.php:28 +#: tpl/page_optm/settings_localization.tpl.php:137 +#: tpl/toolbox/beta_test.tpl.php:49 msgid "Example" msgstr "" -#: tpl/page_optm/settings_localization.tpl.php:132 +#: tpl/page_optm/settings_localization.tpl.php:141 msgid "Please thoroughly test each JS file you add to ensure it functions as expected." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:30 +#: tpl/page_optm/settings_media.tpl.php:40 msgid "Load images only when they enter the viewport." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:31 -#: tpl/page_optm/settings_media.tpl.php:226 +#: tpl/page_optm/settings_media.tpl.php:41 +#: tpl/page_optm/settings_media.tpl.php:235 msgid "This can improve page loading time by reducing initial HTTP requests." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:35 +#: tpl/page_optm/settings_media.tpl.php:45 msgid "Adding Style to Your Lazy-Loaded Images" msgstr "" -#: tpl/page_optm/settings_media.tpl.php:49 +#: tpl/page_optm/settings_media.tpl.php:59 msgid "Specify a base64 image to be used as a simple placeholder while images finish loading." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:50 +#: tpl/page_optm/settings_media.tpl.php:60 msgid "This can be predefined in %2$s as well using constant %1$s, with this setting taking priority." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:51 +#: tpl/page_optm/settings_media.tpl.php:61 msgid "By default a gray image placeholder %s will be used." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:52 +#: tpl/page_optm/settings_media.tpl.php:62 msgid "For example, %s can be used for a transparent placeholder." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:66 +#: tpl/page_optm/settings_media.tpl.php:76 msgid "Responsive image placeholders can help to reduce layout reshuffle when images are loaded." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:67 +#: tpl/page_optm/settings_media.tpl.php:77 msgid "This will generate the placeholder with same dimensions as the image if it has the width and height attributes." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:80 +#: tpl/page_optm/settings_media.tpl.php:90 msgid "Specify an SVG to be used as a placeholder when generating locally." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:81 +#: tpl/page_optm/settings_media.tpl.php:91 msgid "It will be converted to a base64 SVG placeholder on-the-fly." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:82 +#: tpl/page_optm/settings_media.tpl.php:92 msgid "Variables %s will be replaced with the corresponding image properties." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:83 +#: tpl/page_optm/settings_media.tpl.php:93 msgid "Variables %s will be replaced with the configured background color." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:97 +#: tpl/page_optm/settings_media.tpl.php:107 msgid "Specify the responsive placeholder SVG color." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:112 +#: tpl/page_optm/settings_media.tpl.php:122 msgid "Use QUIC.cloud LQIP (Low Quality Image Placeholder) generator service for responsive image previews while loading." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:113 +#: tpl/page_optm/settings_media.tpl.php:123 msgid "Keep this off to use plain color placeholders." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:127 +#: tpl/page_optm/settings_media.tpl.php:137 msgid "Specify the quality when generating LQIP." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:128 +#: tpl/page_optm/settings_media.tpl.php:138 msgid "Larger number will generate higher resolution quality placeholder, but will result in larger files which will increase page size and consume more points." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:131 +#: tpl/page_optm/settings_media.tpl.php:141 msgid "Changes to this setting do not apply to already-generated LQIPs. To regenerate existing LQIPs, please %s first from the admin bar menu." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:144 +#: tpl/page_optm/settings_media.tpl.php:154 msgid "pixels" msgstr "" -#: tpl/page_optm/settings_media.tpl.php:146 +#: tpl/page_optm/settings_media.tpl.php:156 msgid "LQIP requests will not be sent for images where both width and height are smaller than these dimensions." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:162 +#: tpl/page_optm/settings_media.tpl.php:172 msgid "Automatically generate LQIP in the background via a cron-based queue." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:165 +#: tpl/page_optm/settings_media.tpl.php:175 msgid "If set to %1$s, before the placeholder is localized, the %2$s configuration will be used." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:170 +#: tpl/page_optm/settings_media.tpl.php:180 msgid "If set to %s this is done in the foreground, which may slow down page load." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:190 +#: tpl/page_optm/settings_media.tpl.php:200 msgid "Size list in queue waiting for cron" msgstr "" -#: tpl/page_optm/settings_media.tpl.php:225 +#: tpl/page_optm/settings_media.tpl.php:234 msgid "Load iframes only when they enter the viewport." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:239 +#: tpl/page_optm/settings_media.tpl.php:248 msgid "Set an explicit width and height on image elements to reduce layout shifts and improve CLS (a Core Web Vitals metric)." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:250 +#: tpl/page_optm/settings_media.tpl.php:259 msgid "Use %1$s to bypass remote image dimension check when %2$s is ON." msgstr "" -#: tpl/page_optm/settings_media.tpl.php:264 +#: tpl/page_optm/settings_media.tpl.php:274 msgid "The image compression quality setting of WordPress out of 100." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:22 +#: tpl/page_optm/settings_media.tpl.php:289 +msgid "Automatically replace large images with scaled versions." +msgstr "" + +#: tpl/page_optm/settings_media.tpl.php:290 +msgid "Scaled size threshold" +msgstr "" + +#: tpl/page_optm/settings_media.tpl.php:296 +msgid "Filter %s available to change threshold." +msgstr "" + +#: tpl/page_optm/settings_media_exc.tpl.php:31 msgid "Listed images will not be lazy loaded." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:25 +#: tpl/page_optm/settings_media_exc.tpl.php:34 msgid "Useful for above-the-fold images causing CLS (a Core Web Vitals metric)." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:29 -#: tpl/page_optm/settings_tuning.tpl.php:61 -#: tpl/page_optm/settings_tuning.tpl.php:82 -#: tpl/page_optm/settings_tuning.tpl.php:103 -#: tpl/page_optm/settings_tuning_css.tpl.php:27 +#: tpl/page_optm/settings_media_exc.tpl.php:38 +#: tpl/page_optm/settings_tuning.tpl.php:70 +#: tpl/page_optm/settings_tuning.tpl.php:91 +#: tpl/page_optm/settings_tuning.tpl.php:112 +#: tpl/page_optm/settings_tuning_css.tpl.php:37 msgid "Elements with attribute %s in html code will be excluded." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:51 +#: tpl/page_optm/settings_media_exc.tpl.php:60 msgid "Images containing these class names will not be lazy loaded." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:66 +#: tpl/page_optm/settings_media_exc.tpl.php:75 msgid "Images having these parent class names will not be lazy loaded." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:80 +#: tpl/page_optm/settings_media_exc.tpl.php:89 msgid "Iframes containing these class names will not be lazy loaded." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:95 +#: tpl/page_optm/settings_media_exc.tpl.php:104 msgid "Iframes having these parent class names will not be lazy loaded." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:109 +#: tpl/page_optm/settings_media_exc.tpl.php:118 msgid "Prevent any lazy load of listed pages." msgstr "" -#: tpl/page_optm/settings_media_exc.tpl.php:123 +#: tpl/page_optm/settings_media_exc.tpl.php:132 msgid "These images will not generate LQIP." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:34 +#: tpl/page_optm/settings_tuning.tpl.php:43 msgid "Listed JS files or inline JS code will be delayed." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:54 -msgid "Listed JS files or inline JS code will not be minified/combined." +#: tpl/page_optm/settings_tuning.tpl.php:63 +msgid "Listed JS files or inline JS code will not be minified or combined." +msgstr "" + +#: tpl/page_optm/settings_tuning.tpl.php:71 +#: tpl/page_optm/settings_tuning.tpl.php:92 +msgid "Predefined list will also be combined with the above settings." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:76 +#: tpl/page_optm/settings_tuning.tpl.php:85 msgid "Listed JS files or inline JS code will not be deferred or delayed." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:97 +#: tpl/page_optm/settings_tuning.tpl.php:106 msgid "Listed JS files or inline JS code will not be optimized by %s." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:117 +#: tpl/page_optm/settings_tuning.tpl.php:126 msgid "Prevent any optimization of listed pages." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:135 +#: tpl/page_optm/settings_tuning.tpl.php:144 msgid "Only optimize pages for guest (not logged in) visitors. If turned this OFF, CSS/JS/CCSS files will be doubled by each user group." msgstr "" -#: tpl/page_optm/settings_tuning.tpl.php:147 +#: tpl/page_optm/settings_tuning.tpl.php:156 msgid "Selected roles will be excluded from all optimizations." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:21 -msgid "Listed CSS files or inline CSS code will not be minified/combined." +#: tpl/page_optm/settings_tuning_css.tpl.php:17 +msgid "Tuning CSS Settings" +msgstr "" + +#: tpl/page_optm/settings_tuning_css.tpl.php:31 +msgid "Listed CSS files or inline CSS code will not be minified or combined." +msgstr "" + +#: tpl/page_optm/settings_tuning_css.tpl.php:38 +msgid "Predefined list will also be combined with the above settings" msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:42 +#: tpl/page_optm/settings_tuning_css.tpl.php:52 msgid "Listed CSS files will be excluded from UCSS and saved to inline." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:57 +#: tpl/page_optm/settings_tuning_css.tpl.php:67 msgid "List the CSS selectors whose styles should always be included in UCSS." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:60 -#: tpl/page_optm/settings_tuning_css.tpl.php:136 +#: tpl/page_optm/settings_tuning_css.tpl.php:70 +#: tpl/page_optm/settings_tuning_css.tpl.php:145 msgid "Wildcard %s supported." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:64 +#: tpl/page_optm/settings_tuning_css.tpl.php:74 msgid "The selector must exist in the CSS. Parent classes in the HTML will not work." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:82 +#: tpl/page_optm/settings_tuning_css.tpl.php:92 msgid "Listed URI will not generate UCSS." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:89 +#: tpl/page_optm/settings_tuning_css.tpl.php:99 msgid "Use %1$s to generate one single UCSS for the pages which page type is %2$s while other page types still per URL." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:90 +#: tpl/page_optm/settings_tuning_css.tpl.php:100 msgid "Use %1$s to bypass UCSS for the pages which page type is %2$s." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:104 +#: tpl/page_optm/settings_tuning_css.tpl.php:113 msgid "List post types where each item of that type should have its own CCSS generated." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:105 +#: tpl/page_optm/settings_tuning_css.tpl.php:114 msgid "For example, if every Page on the site has different formatting, enter %s in the box. Separate critical CSS files will be stored for every Page on the site." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:119 +#: tpl/page_optm/settings_tuning_css.tpl.php:128 msgid "Separate critical CSS files will be generated for paths containing these strings." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:133 +#: tpl/page_optm/settings_tuning_css.tpl.php:142 msgid "List the CSS selectors whose styles should always be included in CCSS." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:140 +#: tpl/page_optm/settings_tuning_css.tpl.php:149 msgid "Selectors must exist in the CSS. Parent classes in the HTML will not work." msgstr "" -#: tpl/page_optm/settings_tuning_css.tpl.php:158 +#: tpl/page_optm/settings_tuning_css.tpl.php:167 msgid "Specify critical CSS rules for above-the-fold content when enabling %s." msgstr "" -#: tpl/page_optm/settings_vpi.tpl.php:30 +#: tpl/page_optm/settings_vpi.tpl.php:37 msgid "When you use Lazy Load, it will delay the loading of all images on a page." msgstr "" -#: tpl/page_optm/settings_vpi.tpl.php:31 +#: tpl/page_optm/settings_vpi.tpl.php:38 msgid "The Viewport Images service detects which images appear above the fold, and excludes them from lazy load." msgstr "" -#: tpl/page_optm/settings_vpi.tpl.php:32 +#: tpl/page_optm/settings_vpi.tpl.php:39 msgid "This enables the page's initial screenful of imagery to be fully displayed without delay." msgstr "" -#: tpl/page_optm/settings_vpi.tpl.php:121 +#: tpl/page_optm/settings_vpi.tpl.php:122 msgid "Enable Viewport Images auto generation cron." msgstr "" -#: tpl/presets/entry.tpl.php:7 +#: tpl/presets/entry.tpl.php:16 msgid "Standard Presets" msgstr "" -#: tpl/presets/entry.tpl.php:8 -#: tpl/toolbox/entry.tpl.php:11 +#: tpl/presets/entry.tpl.php:17 +#: tpl/toolbox/entry.tpl.php:20 msgid "Import / Export" msgstr "" -#: tpl/presets/entry.tpl.php:15 +#: tpl/presets/entry.tpl.php:23 msgid "LiteSpeed Cache Configuration Presets" msgstr "" -#: tpl/presets/standard.tpl.php:9 +#: tpl/presets/standard.tpl.php:17 msgid "Essentials" msgstr "" -#: tpl/presets/standard.tpl.php:11 +#: tpl/presets/standard.tpl.php:19 msgid "Default Cache" msgstr "" -#: tpl/presets/standard.tpl.php:12 +#: tpl/presets/standard.tpl.php:20 msgid "Higher TTL" msgstr "" -#: tpl/presets/standard.tpl.php:16 +#: tpl/presets/standard.tpl.php:24 msgid "This no-risk preset is appropriate for all websites. Good for new users, simple websites, or cache-oriented development." msgstr "" -#: tpl/presets/standard.tpl.php:17 -msgid "A Domain Key is not required to use this preset. Only basic caching features are enabled." +#: tpl/presets/standard.tpl.php:25 +msgid "A QUIC.cloud connection is not required to use this preset. Only basic caching features are enabled." msgstr "" -#: tpl/presets/standard.tpl.php:22 -#: tpl/toolbox/settings-debug.tpl.php:86 +#: tpl/presets/standard.tpl.php:29 +#: tpl/toolbox/settings-debug.tpl.php:117 msgid "Basic" msgstr "" -#: tpl/presets/standard.tpl.php:24 +#: tpl/presets/standard.tpl.php:31 msgid "Everything in Essentials, Plus" msgstr "" -#: tpl/presets/standard.tpl.php:26 +#: tpl/presets/standard.tpl.php:33 msgid "Mobile Cache" msgstr "" -#: tpl/presets/standard.tpl.php:29 +#: tpl/presets/standard.tpl.php:36 msgid "This low-risk preset introduces basic optimizations for speed and user experience. Appropriate for enthusiastic beginners." msgstr "" -#: tpl/presets/standard.tpl.php:30 -msgid "A Domain Key is required to use this preset. Includes optimizations known to improve site score in page speed measurement tools." +#: tpl/presets/standard.tpl.php:37 +msgid "A QUIC.cloud connection is required to use this preset. Includes optimizations known to improve site score in page speed measurement tools." msgstr "" -#: tpl/presets/standard.tpl.php:35 +#: tpl/presets/standard.tpl.php:41 msgid "Advanced (Recommended)" msgstr "" -#: tpl/presets/standard.tpl.php:37 +#: tpl/presets/standard.tpl.php:43 msgid "Everything in Basic, Plus" msgstr "" -#: tpl/presets/standard.tpl.php:38 +#: tpl/presets/standard.tpl.php:44 msgid "Guest Mode and Guest Optimization" msgstr "" -#: tpl/presets/standard.tpl.php:39 +#: tpl/presets/standard.tpl.php:45 msgid "CSS, JS and HTML Minification" msgstr "" -#: tpl/presets/standard.tpl.php:41 +#: tpl/presets/standard.tpl.php:47 msgid "JS Defer for both external and inline JS" msgstr "" -#: tpl/presets/standard.tpl.php:42 +#: tpl/presets/standard.tpl.php:48 msgid "DNS Prefetch for static files" msgstr "" -#: tpl/presets/standard.tpl.php:44 +#: tpl/presets/standard.tpl.php:50 msgid "Remove Query Strings from Static Files" msgstr "" -#: tpl/presets/standard.tpl.php:49 +#: tpl/presets/standard.tpl.php:55 msgid "This preset is good for most websites, and is unlikely to cause conflicts. Any CSS or JS conflicts may be resolved with Page Optimization > Tuning tools." msgstr "" -#: tpl/presets/standard.tpl.php:50 -#: tpl/presets/standard.tpl.php:65 -msgid "A Domain Key is required to use this preset. Includes many optimizations known to improve page speed scores." +#: tpl/presets/standard.tpl.php:56 +#: tpl/presets/standard.tpl.php:70 +msgid "A QUIC.cloud connection is required to use this preset. Includes many optimizations known to improve page speed scores." msgstr "" -#: tpl/presets/standard.tpl.php:55 +#: tpl/presets/standard.tpl.php:60 msgid "Aggressive" msgstr "" -#: tpl/presets/standard.tpl.php:57 +#: tpl/presets/standard.tpl.php:62 msgid "Everything in Advanced, Plus" msgstr "" -#: tpl/presets/standard.tpl.php:58 +#: tpl/presets/standard.tpl.php:63 msgid "CSS & JS Combine" msgstr "" -#: tpl/presets/standard.tpl.php:59 +#: tpl/presets/standard.tpl.php:64 msgid "Asynchronous CSS Loading with Critical CSS" msgstr "" -#: tpl/presets/standard.tpl.php:60 +#: tpl/presets/standard.tpl.php:65 msgid "Removed Unused CSS for Users" msgstr "" -#: tpl/presets/standard.tpl.php:61 +#: tpl/presets/standard.tpl.php:66 msgid "Lazy Load for Iframes" msgstr "" -#: tpl/presets/standard.tpl.php:64 +#: tpl/presets/standard.tpl.php:69 msgid "This preset might work out of the box for some websites, but be sure to test! Some CSS or JS exclusions may be necessary in Page Optimization > Tuning." msgstr "" -#: tpl/presets/standard.tpl.php:70 +#: tpl/presets/standard.tpl.php:74 msgid "Extreme" msgstr "" -#: tpl/presets/standard.tpl.php:72 +#: tpl/presets/standard.tpl.php:76 msgid "Everything in Aggressive, Plus" msgstr "" -#: tpl/presets/standard.tpl.php:73 +#: tpl/presets/standard.tpl.php:77 msgid "Lazy Load for Images" msgstr "" -#: tpl/presets/standard.tpl.php:74 +#: tpl/presets/standard.tpl.php:78 msgid "Viewport Image Generation" msgstr "" -#: tpl/presets/standard.tpl.php:75 +#: tpl/presets/standard.tpl.php:79 msgid "JS Delayed" msgstr "" -#: tpl/presets/standard.tpl.php:76 +#: tpl/presets/standard.tpl.php:80 msgid "Inline JS added to Combine" msgstr "" -#: tpl/presets/standard.tpl.php:77 +#: tpl/presets/standard.tpl.php:81 msgid "Inline CSS added to Combine" msgstr "" -#: tpl/presets/standard.tpl.php:80 +#: tpl/presets/standard.tpl.php:84 msgid "This preset almost certainly will require testing and exclusions for some CSS, JS and Lazy Loaded images. Pay special attention to logos, or HTML-based slider images." msgstr "" -#: tpl/presets/standard.tpl.php:81 -msgid "A Domain Key is required to use this preset. Enables the maximum level of optimizations for improved page speed scores." +#: tpl/presets/standard.tpl.php:85 +msgid "A QUIC.cloud connection is required to use this preset. Enables the maximum level of optimizations for improved page speed scores." msgstr "" -#: tpl/presets/standard.tpl.php:88 +#: tpl/presets/standard.tpl.php:92 msgid "LiteSpeed Cache Standard Presets" msgstr "" -#: tpl/presets/standard.tpl.php:92 +#: tpl/presets/standard.tpl.php:96 msgid "Use an official LiteSpeed-designed Preset to configure your site in one click. Try no-risk caching essentials, extreme optimization, or something in between." msgstr "" -#: tpl/presets/standard.tpl.php:117 +#: tpl/presets/standard.tpl.php:121 msgid "Who should use this preset?" msgstr "" -#: tpl/presets/standard.tpl.php:127 +#: tpl/presets/standard.tpl.php:131 msgid "This will back up your current settings and replace them with the %1$s preset settings. Do you want to continue?" msgstr "" -#: tpl/presets/standard.tpl.php:129 +#: tpl/presets/standard.tpl.php:133 msgid "Apply Preset" msgstr "" -#: tpl/presets/standard.tpl.php:148 +#: tpl/presets/standard.tpl.php:152 msgid "unknown" msgstr "" -#: tpl/presets/standard.tpl.php:159 +#: tpl/presets/standard.tpl.php:163 msgid "History" msgstr "" -#: tpl/presets/standard.tpl.php:169 +#: tpl/presets/standard.tpl.php:173 msgid "Error: Failed to apply the settings %1$s" msgstr "" -#: tpl/presets/standard.tpl.php:171 +#: tpl/presets/standard.tpl.php:175 msgid "Restored backup settings %1$s" msgstr "" -#: tpl/presets/standard.tpl.php:174 +#: tpl/presets/standard.tpl.php:178 msgid "Applied the %1$s preset %2$s" msgstr "" -#: tpl/presets/standard.tpl.php:185 +#: tpl/presets/standard.tpl.php:189 msgid "Backup created %1$s before applying the %2$s preset" msgstr "" -#: tpl/presets/standard.tpl.php:189 +#: tpl/presets/standard.tpl.php:193 msgid "This will restore the backup settings created %1$s before applying the %2$s preset. Any changes made since then will be lost. Do you want to continue?" msgstr "" -#: tpl/presets/standard.tpl.php:191 +#: tpl/presets/standard.tpl.php:195 msgid "Restore Settings" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:23 +#: tpl/toolbox/beta_test.tpl.php:34 msgid "Try GitHub Version" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:27 +#: tpl/toolbox/beta_test.tpl.php:41 +msgid "LiteSpeed Cache is disabled. This functionality will not work." +msgstr "" + +#: tpl/toolbox/beta_test.tpl.php:46 msgid "Use this section to switch plugin versions. To beta test a GitHub commit, enter the commit URL in the field below." msgstr "" -#: tpl/toolbox/beta_test.tpl.php:32 +#: tpl/toolbox/beta_test.tpl.php:55 msgid "Use latest GitHub Dev commit" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:34 +#: tpl/toolbox/beta_test.tpl.php:59 msgid "Use latest GitHub Master commit" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:36 -#: tpl/toolbox/beta_test.tpl.php:52 +#: tpl/toolbox/beta_test.tpl.php:63 +#: tpl/toolbox/beta_test.tpl.php:79 msgid "Use latest WordPress release version" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:36 +#: tpl/toolbox/beta_test.tpl.php:63 msgid "OR" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:47 +#: tpl/toolbox/beta_test.tpl.php:71 msgid "Downgrade not recommended. May cause fatal error due to refactored code." msgstr "" -#: tpl/toolbox/beta_test.tpl.php:51 +#: tpl/toolbox/beta_test.tpl.php:76 msgid "Press the %s button to use the most recent GitHub commit. Master is for release candidate & Dev is for experimental testing." msgstr "" -#: tpl/toolbox/beta_test.tpl.php:51 +#: tpl/toolbox/beta_test.tpl.php:76 msgid "Use latest GitHub Dev/Master commit" msgstr "" -#: tpl/toolbox/beta_test.tpl.php:52 +#: tpl/toolbox/beta_test.tpl.php:79 msgid "Press the %s button to stop beta testing and go back to the current release from the WordPress Plugin Directory." msgstr "" -#: tpl/toolbox/beta_test.tpl.php:57 +#: tpl/toolbox/beta_test.tpl.php:83 msgid "In order to avoid an upgrade error, you must be using %1$s or later before you can upgrade to %2$s versions." msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:38 +#: tpl/toolbox/edit_htaccess.tpl.php:41 msgid "LiteSpeed Cache View .htaccess" msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:43 +#: tpl/toolbox/edit_htaccess.tpl.php:46 msgid ".htaccess Path" msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:50 +#: tpl/toolbox/edit_htaccess.tpl.php:53 msgid "Frontend .htaccess Path" msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:55 -#: tpl/toolbox/edit_htaccess.tpl.php:73 +#: tpl/toolbox/edit_htaccess.tpl.php:58 +#: tpl/toolbox/edit_htaccess.tpl.php:76 msgid "Default path is" msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:59 -#: tpl/toolbox/edit_htaccess.tpl.php:77 +#: tpl/toolbox/edit_htaccess.tpl.php:62 +#: tpl/toolbox/edit_htaccess.tpl.php:80 msgid "PHP Constant %s is supported." msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:60 -#: tpl/toolbox/edit_htaccess.tpl.php:78 +#: tpl/toolbox/edit_htaccess.tpl.php:63 +#: tpl/toolbox/edit_htaccess.tpl.php:81 msgid "You can use this code %1$s in %2$s to specify the htaccess file path." msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:68 +#: tpl/toolbox/edit_htaccess.tpl.php:71 msgid "Backend .htaccess Path" msgstr "" -#: tpl/toolbox/edit_htaccess.tpl.php:88 +#: tpl/toolbox/edit_htaccess.tpl.php:91 msgid "Current %s Contents" msgstr "" -#: tpl/toolbox/entry.tpl.php:15 +#: tpl/toolbox/entry.tpl.php:24 msgid "View .htaccess" msgstr "" -#: tpl/toolbox/entry.tpl.php:19 +#: tpl/toolbox/entry.tpl.php:28 msgid "Heartbeat" msgstr "" -#: tpl/toolbox/entry.tpl.php:20 +#: tpl/toolbox/entry.tpl.php:29 msgid "Report" msgstr "" -#: tpl/toolbox/entry.tpl.php:24 -#: tpl/toolbox/settings-debug.tpl.php:24 +#: tpl/toolbox/entry.tpl.php:33 +#: tpl/toolbox/settings-debug.tpl.php:55 msgid "Debug Settings" msgstr "" -#: tpl/toolbox/entry.tpl.php:25 +#: tpl/toolbox/entry.tpl.php:34 msgid "Log View" msgstr "" -#: tpl/toolbox/entry.tpl.php:26 +#: tpl/toolbox/entry.tpl.php:35 msgid "Beta Test" msgstr "" -#: tpl/toolbox/entry.tpl.php:33 +#: tpl/toolbox/entry.tpl.php:41 msgid "LiteSpeed Cache Toolbox" msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:10 +#: tpl/toolbox/heartbeat.tpl.php:19 msgid "Heartbeat Control" msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:16 +#: tpl/toolbox/heartbeat.tpl.php:26 msgid "Disable WordPress interval heartbeat to reduce server load." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:19 +#: tpl/toolbox/heartbeat.tpl.php:28 msgid "Disabling this may cause WordPress tasks triggered by AJAX to stop working." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:35 +#: tpl/toolbox/heartbeat.tpl.php:43 msgid "Turn ON to control heartbeat on frontend." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:48 -#: tpl/toolbox/heartbeat.tpl.php:78 -#: tpl/toolbox/heartbeat.tpl.php:108 +#: tpl/toolbox/heartbeat.tpl.php:56 +#: tpl/toolbox/heartbeat.tpl.php:86 +#: tpl/toolbox/heartbeat.tpl.php:116 msgid "Specify the %s heartbeat interval in seconds." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:49 +#: tpl/toolbox/heartbeat.tpl.php:57 +#: tpl/toolbox/heartbeat.tpl.php:87 +#: tpl/toolbox/heartbeat.tpl.php:117 msgid "WordPress valid interval is %s seconds." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:50 -#: tpl/toolbox/heartbeat.tpl.php:80 -#: tpl/toolbox/heartbeat.tpl.php:110 +#: tpl/toolbox/heartbeat.tpl.php:58 +#: tpl/toolbox/heartbeat.tpl.php:88 +#: tpl/toolbox/heartbeat.tpl.php:118 msgid "Set to %1$s to forbid heartbeat on %2$s." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:65 +#: tpl/toolbox/heartbeat.tpl.php:73 msgid "Turn ON to control heartbeat on backend." msgstr "" -#: tpl/toolbox/heartbeat.tpl.php:79 -#: tpl/toolbox/heartbeat.tpl.php:109 -msgid "WordPress valid interval is %s seconds" -msgstr "" - -#: tpl/toolbox/heartbeat.tpl.php:95 +#: tpl/toolbox/heartbeat.tpl.php:103 msgid "Turn ON to control heartbeat in backend editor." msgstr "" -#: tpl/toolbox/import_export.tpl.php:10 +#: tpl/toolbox/import_export.tpl.php:19 msgid "Export Settings" msgstr "" -#: tpl/toolbox/import_export.tpl.php:15 +#: tpl/toolbox/import_export.tpl.php:25 msgid "Export" msgstr "" -#: tpl/toolbox/import_export.tpl.php:20 +#: tpl/toolbox/import_export.tpl.php:31 msgid "Last exported" msgstr "" -#: tpl/toolbox/import_export.tpl.php:25 +#: tpl/toolbox/import_export.tpl.php:36 msgid "This will export all current LiteSpeed Cache settings and save them as a file." msgstr "" -#: tpl/toolbox/import_export.tpl.php:28 +#: tpl/toolbox/import_export.tpl.php:40 msgid "Import Settings" msgstr "" -#: tpl/toolbox/import_export.tpl.php:36 +#: tpl/toolbox/import_export.tpl.php:48 msgid "Import" msgstr "" -#: tpl/toolbox/import_export.tpl.php:42 +#: tpl/toolbox/import_export.tpl.php:54 msgid "Last imported" msgstr "" -#: tpl/toolbox/import_export.tpl.php:47 +#: tpl/toolbox/import_export.tpl.php:59 msgid "This will import settings from a file and override all current LiteSpeed Cache settings." msgstr "" -#: tpl/toolbox/import_export.tpl.php:50 +#: tpl/toolbox/import_export.tpl.php:63 msgid "Reset All Settings" msgstr "" -#: tpl/toolbox/import_export.tpl.php:51 +#: tpl/toolbox/import_export.tpl.php:67 msgid "This will reset all settings to default settings." msgstr "" -#: tpl/toolbox/import_export.tpl.php:53 +#: tpl/toolbox/import_export.tpl.php:70 msgid "Are you sure you want to reset all settings back to the default settings?" msgstr "" -#: tpl/toolbox/import_export.tpl.php:54 +#: tpl/toolbox/import_export.tpl.php:71 msgid "Reset Settings" msgstr "" -#: tpl/toolbox/log_viewer.tpl.php:16 +#: tpl/toolbox/log_viewer.tpl.php:23 msgid "Purge Log" msgstr "" -#: tpl/toolbox/log_viewer.tpl.php:21 +#: tpl/toolbox/log_viewer.tpl.php:28 msgid "Crawler Log" msgstr "" -#: tpl/toolbox/log_viewer.tpl.php:79 -msgid "Clear Logs" +#: tpl/toolbox/log_viewer.tpl.php:35 +msgid "LiteSpeed Logs" msgstr "" -#: tpl/toolbox/log_viewer.tpl.php:96 -msgid "Copy Log" +#: tpl/toolbox/log_viewer.tpl.php:46 +#: tpl/toolbox/log_viewer.tpl.php:75 +msgid "Clear Logs" msgstr "" -#: tpl/toolbox/log_viewer.tpl.php:100 -#: tpl/toolbox/report.tpl.php:54 +#: tpl/toolbox/log_viewer.tpl.php:64 +#: tpl/toolbox/report.tpl.php:62 msgid "Click to copy" msgstr "" -#: tpl/toolbox/log_viewer.tpl.php:133 -msgid "LiteSpeed Logs" +#: tpl/toolbox/log_viewer.tpl.php:65 +msgid "Copy Log" msgstr "" -#: tpl/toolbox/purge.tpl.php:9 +#: tpl/toolbox/purge.tpl.php:17 msgid "Purge Front Page" msgstr "" -#: tpl/toolbox/purge.tpl.php:10 +#: tpl/toolbox/purge.tpl.php:18 msgid "This will Purge Front Page only" msgstr "" -#: tpl/toolbox/purge.tpl.php:15 +#: tpl/toolbox/purge.tpl.php:23 msgid "Purge Pages" msgstr "" -#: tpl/toolbox/purge.tpl.php:16 +#: tpl/toolbox/purge.tpl.php:24 msgid "This will Purge Pages only" msgstr "" -#: tpl/toolbox/purge.tpl.php:23 +#: tpl/toolbox/purge.tpl.php:32 msgid "Purge %s Error" msgstr "" -#: tpl/toolbox/purge.tpl.php:24 +#: tpl/toolbox/purge.tpl.php:33 msgid "Purge %s error pages" msgstr "" -#: tpl/toolbox/purge.tpl.php:31 +#: tpl/toolbox/purge.tpl.php:41 msgid "Purge the LiteSpeed cache entries created by this plugin" msgstr "" -#: tpl/toolbox/purge.tpl.php:37 +#: tpl/toolbox/purge.tpl.php:48 msgid "This will purge all minified/combined CSS/JS entries only" msgstr "" -#: tpl/toolbox/purge.tpl.php:45 +#: tpl/toolbox/purge.tpl.php:56 msgid "Purge all the object caches" msgstr "" -#: tpl/toolbox/purge.tpl.php:54 +#: tpl/toolbox/purge.tpl.php:65 msgid "Reset the entire opcode cache" msgstr "" -#: tpl/toolbox/purge.tpl.php:63 +#: tpl/toolbox/purge.tpl.php:74 msgid "This will delete all generated critical CSS files" msgstr "" -#: tpl/toolbox/purge.tpl.php:72 +#: tpl/toolbox/purge.tpl.php:83 msgid "This will delete all generated unique CSS files" msgstr "" -#: tpl/toolbox/purge.tpl.php:81 +#: tpl/toolbox/purge.tpl.php:92 msgid "This will delete all localized resources" msgstr "" -#: tpl/toolbox/purge.tpl.php:90 +#: tpl/toolbox/purge.tpl.php:101 msgid "This will delete all generated image LQIP placeholder files" msgstr "" -#: tpl/toolbox/purge.tpl.php:99 +#: tpl/toolbox/purge.tpl.php:110 msgid "This will delete all cached Gravatar files" msgstr "" -#: tpl/toolbox/purge.tpl.php:108 +#: tpl/toolbox/purge.tpl.php:118 msgid "Purge the cache entries created by this plugin except for Critical CSS & Unique CSS & LQIP caches" msgstr "" -#: tpl/toolbox/purge.tpl.php:117 +#: tpl/toolbox/purge.tpl.php:127 msgid "Empty Entire Cache" msgstr "" -#: tpl/toolbox/purge.tpl.php:118 -msgid "Clears all cache entries related to this site, including other web applications." +#: tpl/toolbox/purge.tpl.php:128 +msgid "Clears all cache entries related to this site, including other web applications." msgstr "" -#: tpl/toolbox/purge.tpl.php:119 +#: tpl/toolbox/purge.tpl.php:128 msgid "This action should only be used if things are cached incorrectly." msgstr "" -#: tpl/toolbox/purge.tpl.php:123 +#: tpl/toolbox/purge.tpl.php:132 msgid "This will clear EVERYTHING inside the cache." msgstr "" -#: tpl/toolbox/purge.tpl.php:124 +#: tpl/toolbox/purge.tpl.php:132 msgid "This may cause heavy load on the server." msgstr "" -#: tpl/toolbox/purge.tpl.php:125 +#: tpl/toolbox/purge.tpl.php:132 msgid "If only the WordPress site should be purged, use Purge All." msgstr "" @@ -5870,199 +6071,212 @@ msgstr "" msgid "Purge By..." msgstr "" -#: tpl/toolbox/purge.tpl.php:178 +#: tpl/toolbox/purge.tpl.php:179 msgid "Select below for \"Purge by\" options." msgstr "" -#: tpl/toolbox/purge.tpl.php:211 +#: tpl/toolbox/purge.tpl.php:188 msgid "Category" msgstr "" -#: tpl/toolbox/purge.tpl.php:220 +#: tpl/toolbox/purge.tpl.php:192 msgid "Post ID" msgstr "" -#: tpl/toolbox/purge.tpl.php:229 +#: tpl/toolbox/purge.tpl.php:196 msgid "Tag" msgstr "" -#: tpl/toolbox/purge.tpl.php:250 +#: tpl/toolbox/purge.tpl.php:205 msgid "Purge pages by category name - e.g. %2$s should be used for the URL %1$s." msgstr "" -#: tpl/toolbox/purge.tpl.php:262 +#: tpl/toolbox/purge.tpl.php:208 msgid "Purge pages by post ID." msgstr "" -#: tpl/toolbox/purge.tpl.php:272 +#: tpl/toolbox/purge.tpl.php:211 msgid "Purge pages by tag name - e.g. %2$s should be used for the URL %1$s." msgstr "" -#: tpl/toolbox/purge.tpl.php:284 +#: tpl/toolbox/purge.tpl.php:214 msgid "Purge pages by relative or full URL." msgstr "" -#: tpl/toolbox/purge.tpl.php:287 +#: tpl/toolbox/purge.tpl.php:215 msgid "e.g. Use %1$s or %2$s." msgstr "" -#: tpl/toolbox/purge.tpl.php:302 +#: tpl/toolbox/purge.tpl.php:225 msgid "Purge List" msgstr "" -#: tpl/toolbox/report.tpl.php:30 +#: tpl/toolbox/report.tpl.php:38 msgid "Send to LiteSpeed" msgstr "" -#: tpl/toolbox/report.tpl.php:32 +#: tpl/toolbox/report.tpl.php:40 msgid "Regenerate and Send a New Report" msgstr "" -#: tpl/toolbox/report.tpl.php:40 +#: tpl/toolbox/report.tpl.php:48 msgid "To generate a passwordless link for LiteSpeed Support Team access, you must install %s." msgstr "" -#: tpl/toolbox/report.tpl.php:43 +#: tpl/toolbox/report.tpl.php:51 msgid "Install DoLogin Security" msgstr "" -#: tpl/toolbox/report.tpl.php:44 +#: tpl/toolbox/report.tpl.php:52 msgid "Go to plugins list" msgstr "" -#: tpl/toolbox/report.tpl.php:50 +#: tpl/toolbox/report.tpl.php:58 msgid "LiteSpeed Report" msgstr "" -#: tpl/toolbox/report.tpl.php:54 +#: tpl/toolbox/report.tpl.php:62 msgid "Last Report Number" msgstr "" -#: tpl/toolbox/report.tpl.php:55 +#: tpl/toolbox/report.tpl.php:63 msgid "Last Report Date" msgstr "" -#: tpl/toolbox/report.tpl.php:58 +#: tpl/toolbox/report.tpl.php:66 msgid "The environment report contains detailed information about the WordPress configuration." msgstr "" -#: tpl/toolbox/report.tpl.php:60 +#: tpl/toolbox/report.tpl.php:68 msgid "If you run into any issues, please refer to the report number in your support message." msgstr "" -#: tpl/toolbox/report.tpl.php:67 +#: tpl/toolbox/report.tpl.php:75 msgid "System Information" msgstr "" -#: tpl/toolbox/report.tpl.php:79 +#: tpl/toolbox/report.tpl.php:87 msgid "Attach PHP info to report. Check this box to insert relevant data from %s." msgstr "" -#: tpl/toolbox/report.tpl.php:91 +#: tpl/toolbox/report.tpl.php:96 msgid "Passwordless Link" msgstr "" -#: tpl/toolbox/report.tpl.php:95 -#: tpl/toolbox/report.tpl.php:97 +#: tpl/toolbox/report.tpl.php:100 +#: tpl/toolbox/report.tpl.php:102 msgid "Generate Link for Current User" msgstr "" -#: tpl/toolbox/report.tpl.php:100 +#: tpl/toolbox/report.tpl.php:105 msgid "To grant wp-admin access to the LiteSpeed Support Team, please generate a passwordless link for the current logged-in user to be sent with the report." msgstr "" -#: tpl/toolbox/report.tpl.php:102 +#: tpl/toolbox/report.tpl.php:107 msgid "Please do NOT share the above passwordless link with anyone." msgstr "" -#: tpl/toolbox/report.tpl.php:103 -msgid "Generated links may be managed under Settings." +#. translators: %s: Link tags +#: tpl/toolbox/report.tpl.php:112 +msgid "Generated links may be managed under %sSettings%s." msgstr "" -#: tpl/toolbox/report.tpl.php:109 +#: tpl/toolbox/report.tpl.php:122 msgid "Notes" msgstr "" -#: tpl/toolbox/report.tpl.php:113 +#: tpl/toolbox/report.tpl.php:126 msgid "Optional" msgstr "" -#: tpl/toolbox/report.tpl.php:114 +#: tpl/toolbox/report.tpl.php:127 msgid "provide more information here to assist the LiteSpeed team with debugging." msgstr "" -#: tpl/toolbox/report.tpl.php:126 +#: tpl/toolbox/report.tpl.php:139 msgid "Send this report to LiteSpeed. Refer to this report number when posting in the WordPress support forum." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:11 +#: tpl/toolbox/settings-debug.tpl.php:19 msgid "Debug Helpers" msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:15 +#: tpl/toolbox/settings-debug.tpl.php:23 msgid "View Site Before Optimization" msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:19 +#: tpl/toolbox/settings-debug.tpl.php:27 msgid "View Site Before Cache" msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:38 +#: tpl/toolbox/settings-debug.tpl.php:37 +msgid "Disable All Features for 24 Hours" +msgstr "" + +#: tpl/toolbox/settings-debug.tpl.php:44 +msgid "Remove `Disable All Feature` Flag Now" +msgstr "" + +#: tpl/toolbox/settings-debug.tpl.php:48 +msgid "LiteSpeed Cache is temporarily disabled until: %s." +msgstr "" + +#: tpl/toolbox/settings-debug.tpl.php:69 msgid "This will disable LSCache and all optimization features for debug purpose." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:49 +#: tpl/toolbox/settings-debug.tpl.php:80 msgid "Admin IP Only" msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:51 +#: tpl/toolbox/settings-debug.tpl.php:82 msgid "Outputs to a series of files in the %s directory." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:52 +#: tpl/toolbox/settings-debug.tpl.php:83 msgid "To prevent filling up the disk, this setting should be OFF when everything is working." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:53 +#: tpl/toolbox/settings-debug.tpl.php:84 msgid "The Admin IP option will only output log messages on requests from admin IPs listed below." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:66 +#: tpl/toolbox/settings-debug.tpl.php:97 msgid "Allows listed IPs (one per line) to perform certain actions from their browsers." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:67 +#: tpl/toolbox/settings-debug.tpl.php:98 msgid "Your IP" msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:73 +#: tpl/toolbox/settings-debug.tpl.php:104 msgid "More information about the available commands can be found here." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:88 +#: tpl/toolbox/settings-debug.tpl.php:119 msgid "Advanced level will log more details." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:99 +#: tpl/toolbox/settings-debug.tpl.php:130 msgid "MB" msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:101 +#: tpl/toolbox/settings-debug.tpl.php:132 msgid "Specify the maximum size of the log file." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:116 +#: tpl/toolbox/settings-debug.tpl.php:147 msgid "Shorten query strings in the debug log to improve readability." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:129 +#: tpl/toolbox/settings-debug.tpl.php:160 msgid "Only log listed pages." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:143 +#: tpl/toolbox/settings-debug.tpl.php:174 msgid "Prevent any debug log of listed pages." msgstr "" -#: tpl/toolbox/settings-debug.tpl.php:157 +#: tpl/toolbox/settings-debug.tpl.php:188 msgid "Prevent writing log entries that include listed strings." msgstr "" diff --git a/lib/css_js_min/minify/css.cls.php b/lib/css_js_min/minify/css.cls.php index b59b2826b..4c85b467b 100644 --- a/lib/css_js_min/minify/css.cls.php +++ b/lib/css_js_min/minify/css.cls.php @@ -1,4 +1,5 @@ */ defined( 'WPINC' ) || exit; diff --git a/lib/urirewriter.cls.php b/lib/urirewriter.cls.php index 0037f59f8..fff2e1143 100644 --- a/lib/urirewriter.cls.php +++ b/lib/urirewriter.cls.php @@ -1,4 +1,5 @@ . */ -defined('WPINC') || exit(); -if (defined('LSCWP_V')) { +defined( 'WPINC' ) || exit(); + +if ( defined( 'LSCWP_V' ) ) { return; } -!defined('LSCWP_V') && define('LSCWP_V', '7.3-b3'); +! defined( 'LSCWP_V' ) && define( 'LSCWP_V', '7.6-b9' ); -!defined('LSCWP_CONTENT_DIR') && define('LSCWP_CONTENT_DIR', WP_CONTENT_DIR); -!defined('LSCWP_DIR') && define('LSCWP_DIR', __DIR__ . '/'); // Full absolute path '/var/www/html/***/wp-content/plugins/litespeed-cache/' or MU -!defined('LSCWP_BASENAME') && define('LSCWP_BASENAME', 'litespeed-cache/litespeed-cache.php'); // LSCWP_BASENAME='litespeed-cache/litespeed-cache.php' +! defined( 'LSCWP_CONTENT_DIR' ) && define( 'LSCWP_CONTENT_DIR', WP_CONTENT_DIR ); +! defined( 'LSCWP_DIR' ) && define( 'LSCWP_DIR', __DIR__ . '/' ); // Full absolute path '/var/www/html/***/wp-content/plugins/litespeed-cache/' or MU +! defined( 'LSCWP_BASENAME' ) && define( 'LSCWP_BASENAME', 'litespeed-cache/litespeed-cache.php' ); // LSCWP_BASENAME='litespeed-cache/litespeed-cache.php' /** * This needs to be before activation because admin-rules.class.php need const `LSCWP_CONTENT_FOLDER` @@ -45,139 +47,164 @@ * * @since 5.2 Auto correct protocol for CONTENT URL */ -$WP_CONTENT_URL = WP_CONTENT_URL; -$site_url = site_url('/'); -if (substr($WP_CONTENT_URL, 0, 5) == 'http:' && substr($site_url, 0, 5) == 'https') { - $WP_CONTENT_URL = str_replace('http://', 'https://', $WP_CONTENT_URL); +$wp_content_url = WP_CONTENT_URL; +$site_url = site_url( '/' ); +if ( 'http:' === substr( $wp_content_url, 0, 5 ) && 'https' === substr( $site_url, 0, 5 ) ) { + $wp_content_url = str_replace( 'http://', 'https://', $wp_content_url ); } -!defined('LSCWP_CONTENT_FOLDER') && define('LSCWP_CONTENT_FOLDER', str_replace($site_url, '', $WP_CONTENT_URL)); // `wp-content` -!defined('LSWCP_PLUGIN_URL') && define('LSWCP_PLUGIN_URL', plugin_dir_url(__FILE__)); // Full URL path '//example.com/wp-content/plugins/litespeed-cache/' +! defined( 'LSCWP_CONTENT_FOLDER' ) && define( 'LSCWP_CONTENT_FOLDER', str_replace( $site_url, '', $wp_content_url ) ); // `wp-content` +unset( $site_url ); +! defined( 'LSWCP_PLUGIN_URL' ) && define( 'LSWCP_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); // Full URL path '//example.com/wp-content/plugins/litespeed-cache/' /** * Static cache files consts * * @since 3.0 */ -!defined('LITESPEED_DATA_FOLDER') && define('LITESPEED_DATA_FOLDER', 'litespeed'); -!defined('LITESPEED_STATIC_URL') && define('LITESPEED_STATIC_URL', $WP_CONTENT_URL . '/' . LITESPEED_DATA_FOLDER); // Full static cache folder URL '//example.com/wp-content/litespeed' -!defined('LITESPEED_STATIC_DIR') && define('LITESPEED_STATIC_DIR', LSCWP_CONTENT_DIR . '/' . LITESPEED_DATA_FOLDER); // Full static cache folder path '/var/www/html/***/wp-content/litespeed' +! defined( 'LITESPEED_DATA_FOLDER' ) && define( 'LITESPEED_DATA_FOLDER', 'litespeed' ); +! defined( 'LITESPEED_STATIC_URL' ) && define( 'LITESPEED_STATIC_URL', $wp_content_url . '/' . LITESPEED_DATA_FOLDER ); // Full static cache folder URL '//example.com/wp-content/litespeed' +unset( $wp_content_url ); +! defined( 'LITESPEED_STATIC_DIR' ) && define( 'LITESPEED_STATIC_DIR', LSCWP_CONTENT_DIR . '/' . LITESPEED_DATA_FOLDER ); // Full static cache folder path '/var/www/html/***/wp-content/litespeed' -!defined('LITESPEED_TIME_OFFSET') && define('LITESPEED_TIME_OFFSET', get_option('gmt_offset') * 60 * 60); +! defined( 'LITESPEED_TIME_OFFSET' ) && define( 'LITESPEED_TIME_OFFSET', get_option( 'gmt_offset' ) * 60 * 60 ); // Placeholder for lazyload img -!defined('LITESPEED_PLACEHOLDER') && define('LITESPEED_PLACEHOLDER', 'data:image/gif;base64,R0lGODdhAQABAPAAAMPDwwAAACwAAAAAAQABAAACAkQBADs='); +! defined( 'LITESPEED_PLACEHOLDER' ) && define( 'LITESPEED_PLACEHOLDER', 'data:image/gif;base64,R0lGODdhAQABAPAAAMPDwwAAACwAAAAAAQABAAACAkQBADs=' ); // Auto register LiteSpeed classes require_once LSCWP_DIR . 'autoload.php'; // Define CLI -if ((defined('WP_CLI') && WP_CLI) || PHP_SAPI == 'cli') { - !defined('LITESPEED_CLI') && define('LITESPEED_CLI', true); +if ( ( defined( 'WP_CLI' ) && constant('WP_CLI') ) || 'cli' === PHP_SAPI ) { + ! defined( 'LITESPEED_CLI' ) && define( 'LITESPEED_CLI', true ); // Register CLI cmd - if (method_exists('WP_CLI', 'add_command')) { - WP_CLI::add_command('litespeed-option', 'LiteSpeed\CLI\Option'); - WP_CLI::add_command('litespeed-purge', 'LiteSpeed\CLI\Purge'); - WP_CLI::add_command('litespeed-online', 'LiteSpeed\CLI\Online'); - WP_CLI::add_command('litespeed-image', 'LiteSpeed\CLI\Image'); - WP_CLI::add_command('litespeed-debug', 'LiteSpeed\CLI\Debug'); - WP_CLI::add_command('litespeed-presets', 'LiteSpeed\CLI\Presets'); - WP_CLI::add_command('litespeed-crawler', 'LiteSpeed\CLI\Crawler'); + if ( method_exists( 'WP_CLI', 'add_command' ) ) { + WP_CLI::add_command( 'litespeed-option', 'LiteSpeed\CLI\Option' ); + WP_CLI::add_command( 'litespeed-purge', 'LiteSpeed\CLI\Purge' ); + WP_CLI::add_command( 'litespeed-online', 'LiteSpeed\CLI\Online' ); + WP_CLI::add_command( 'litespeed-image', 'LiteSpeed\CLI\Image' ); + WP_CLI::add_command( 'litespeed-debug', 'LiteSpeed\CLI\Debug' ); + WP_CLI::add_command( 'litespeed-presets', 'LiteSpeed\CLI\Presets' ); + WP_CLI::add_command( 'litespeed-crawler', 'LiteSpeed\CLI\Crawler' ); + WP_CLI::add_command( 'litespeed-database', 'LiteSpeed\CLI\Database' ); } } // Server type -if (!defined('LITESPEED_SERVER_TYPE')) { - if (isset($_SERVER['HTTP_X_LSCACHE']) && $_SERVER['HTTP_X_LSCACHE']) { - define('LITESPEED_SERVER_TYPE', 'LITESPEED_SERVER_ADC'); - } elseif (isset($_SERVER['LSWS_EDITION']) && strpos($_SERVER['LSWS_EDITION'], 'Openlitespeed') === 0) { - define('LITESPEED_SERVER_TYPE', 'LITESPEED_SERVER_OLS'); - } elseif (isset($_SERVER['SERVER_SOFTWARE']) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed') { - define('LITESPEED_SERVER_TYPE', 'LITESPEED_SERVER_ENT'); +if ( ! defined( 'LITESPEED_SERVER_TYPE' ) ) { + $http_x_lscache = isset( $_SERVER['HTTP_X_LSCACHE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_LSCACHE'] ) ) : ''; + $lsws_edition = isset( $_SERVER['LSWS_EDITION'] ) ? sanitize_text_field( wp_unslash( $_SERVER['LSWS_EDITION'] ) ) : ''; + $server_software = isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : ''; + + if ( $http_x_lscache ) { + define( 'LITESPEED_SERVER_TYPE', 'LITESPEED_SERVER_ADC' ); + } elseif ( 0 === strpos( $lsws_edition, 'Openlitespeed' ) ) { + define( 'LITESPEED_SERVER_TYPE', 'LITESPEED_SERVER_OLS' ); + } elseif ( 'LiteSpeed' === $server_software ) { + define( 'LITESPEED_SERVER_TYPE', 'LITESPEED_SERVER_ENT' ); } else { - define('LITESPEED_SERVER_TYPE', 'NONE'); + define( 'LITESPEED_SERVER_TYPE', 'NONE' ); } } // Checks if caching is allowed via server variable -if (!empty($_SERVER['X-LSCACHE']) || LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_ADC' || defined('LITESPEED_CLI')) { - !defined('LITESPEED_ALLOWED') && define('LITESPEED_ALLOWED', true); +if ( ! empty( $_SERVER['X-LSCACHE'] ) || 'LITESPEED_SERVER_ADC' === LITESPEED_SERVER_TYPE || defined( 'LITESPEED_CLI' ) ) { + ! defined( 'LITESPEED_ALLOWED' ) && define( 'LITESPEED_ALLOWED', true ); } // ESI const definition -if (!defined('LSWCP_ESI_SUPPORT')) { - define('LSWCP_ESI_SUPPORT', LITESPEED_SERVER_TYPE !== 'LITESPEED_SERVER_OLS' ? true : false); +if ( ! defined( 'LSWCP_ESI_SUPPORT' ) ) { + define( 'LSWCP_ESI_SUPPORT', LITESPEED_SERVER_TYPE !== 'LITESPEED_SERVER_OLS' ); } -if (!defined('LSWCP_TAG_PREFIX')) { - define('LSWCP_TAG_PREFIX', substr(md5(LSCWP_DIR), -3)); +if ( ! defined( 'LSWCP_TAG_PREFIX' ) ) { + define( 'LSWCP_TAG_PREFIX', substr( md5( LSCWP_DIR ), -3 ) ); } -/** - * Handle exception - */ -if (!function_exists('litespeed_exception_handler')) { +if ( ! function_exists( 'litespeed_exception_handler' ) ) { + /** + * Handle exception + * + * @param int $errno Error number. + * @param string $errstr Error string. + * @param string $errfile Error file. + * @param int $errline Error line. + * @throws \ErrorException When an error is encountered. + */ function litespeed_exception_handler( $errno, $errstr, $errfile, $errline ) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); + throw new \ErrorException( + esc_html( $errstr ), + 0, + absint( $errno ), + esc_html( $errfile ), + absint( $errline ) + ); } } -/** - * Overwrite the WP nonce funcs outside of LiteSpeed namespace - * - * @since 3.0 - */ -if (!function_exists('litespeed_define_nonce_func')) { +if ( ! function_exists( 'litespeed_define_nonce_func' ) ) { + /** + * Overwrite the WP nonce funcs outside of LiteSpeed namespace + * + * @since 3.0 + */ function litespeed_define_nonce_func() { /** * If the nonce is in none_actions filter, convert it to ESI + * + * @param mixed $action Action name or -1. + * @return string */ function wp_create_nonce( $action = -1 ) { - if (!defined('LITESPEED_DISABLE_ALL') || !LITESPEED_DISABLE_ALL) { - $control = \LiteSpeed\ESI::cls()->is_nonce_action($action); - if ($control !== null) { + if ( ! defined( 'LITESPEED_DISABLE_ALL' ) || ! LITESPEED_DISABLE_ALL ) { + $control = \LiteSpeed\ESI::cls()->is_nonce_action( $action ); + if ( null !== $control ) { $params = array( 'action' => $action, ); - return \LiteSpeed\ESI::cls()->sub_esi_block('nonce', 'wp_create_nonce ' . $action, $params, $control, true, true, true); + return \LiteSpeed\ESI::cls()->sub_esi_block( 'nonce', 'wp_create_nonce ' . $action, $params, $control, true, true, true ); } } - return wp_create_nonce_litespeed_esi($action); + return wp_create_nonce_litespeed_esi( $action ); } /** * Ori WP wp_create_nonce + * + * @param mixed $action Action name or -1. + * @return string */ function wp_create_nonce_litespeed_esi( $action = -1 ) { $uid = get_current_user_id(); - if (!$uid) { + if ( ! $uid ) { /** This filter is documented in wp-includes/pluggable.php */ - $uid = apply_filters('nonce_user_logged_out', $uid, $action); + $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); } $token = wp_get_session_token(); $i = wp_nonce_tick(); - return substr(wp_hash($i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10); + return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); } } } -/** - * Begins execution of the plugin. - * - * @since 1.0.0 - */ -if (!function_exists('run_litespeed_cache')) { +if ( ! function_exists( 'run_litespeed_cache' ) ) { + /** + * Begins execution of the plugin. + * + * @since 1.0.0 + */ function run_litespeed_cache() { // Check minimum PHP requirements, which is 7.2 at the moment. - if (version_compare(PHP_VERSION, '7.2.0', '<')) { + if ( version_compare( PHP_VERSION, '7.2.0', '<' ) ) { return; } // Check minimum WP requirements, which is 5.3 at the moment. - if (version_compare($GLOBALS['wp_version'], '5.3', '<')) { + if ( version_compare( $GLOBALS['wp_version'], '5.3', '<' ) ) { return; } diff --git a/package.json b/package.json index ff73be976..780d84d57 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,10 @@ "description": "High-performance page caching and site optimization from LiteSpeed", "license": "GPLv3", "scripts": { - "format-check": "vendor/bin/phpcs --standard=phpcs.ruleset.xml tpl/ autoload.php", - "format-check-bk": "vendor/bin/phpcs --standard=phpcs.ruleset.xml cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php", + "format-check": "vendor/bin/phpcs --standard=phpcs.ruleset.xml cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php", "install-composer-packages": "composer require --dev squizlabs/php_codesniffer:^3.12 wp-coding-standards/wpcs:^3.1 dealerdirect/phpcodesniffer-composer-installer:^1.0 && vendor/bin/phpcs --config-set installed_paths vendor/wp-coding-standards/wpcs,vendor/phpcsstandards/phpcsutils,vendor/phpcsstandards/phpcsextra", - "sniff-check": "vendor/bin/phpcs --standard=phpcs.ruleset.xml tpl/ autoload.php", - "sniff-check-bk": "vendor/bin/phpcs --standard=phpcs.ruleset.xml cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php", - "wpformat": "vendor/bin/phpcbf --standard=phpcs.ruleset.xml tpl/ autoload.php", - "wpformat-bk": "vendor/bin/phpcbf --standard=phpcs.ruleset.xml cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php" + "sniff-check": "vendor/bin/phpcs --standard=phpcs.ruleset.xml cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php", + "wpformat": "vendor/bin/phpcbf --standard=phpcs.ruleset.xml cli/ lib/ src/ tpl/ thirdparty autoload.php litespeed-cache.php" }, "devDependencies": { "@prettier/plugin-php": "^0.21.0", diff --git a/phpcs.ruleset.xml b/phpcs.ruleset.xml index 608f19c8f..de4e3ab7e 100644 --- a/phpcs.ruleset.xml +++ b/phpcs.ruleset.xml @@ -50,7 +50,17 @@ 0 - + + + + + + + + 0 + + + 0 @@ -59,14 +69,17 @@ 0 + + 0 + - - - + cli/ + lib/ + src/ tpl/ - + thirdparty/ autoload.php - + litespeed-cache.php \ No newline at end of file diff --git a/readme.txt b/readme.txt index eb0685b5c..ebcd3e6f6 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: caching, optimize, performance, pagespeed, seo, image optimize, object cac Requires at least: 5.3 Requires PHP: 7.2 Tested up to: 6.8 -Stable tag: 7.2 +Stable tag: 7.5.0.1 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl.html @@ -132,7 +132,9 @@ This plugin includes some suggested text that you can add to your site's Privacy **For your own information:** LiteSpeed Cache for WordPress potentially stores a duplicate copy of every web page on display on your site. The pages are stored locally on the system where LiteSpeed server software is installed and are not transferred to or accessed by LiteSpeed employees in any way, except as necessary in providing routine technical support if you request it. All cache files are temporary, and may easily be purged before their natural expiration, if necessary, via a Purge All command. It is up to individual site administrators to come up with their own cache expiration rules. -In addition to caching, our WordPress plugin has online features provided by QUIC.cloud for Image Optimization, CSS Optimization and Low Quality Image Placeholder services. When one of those optimizations are requested, data is transmitted to a remote QUIC.cloud server, processed, and then transmitted back for use on your site. Now if using the QUIC.cloud CDN it uses LSCache technologies to access your site then host your site to others globally and also your data is not transferred to or accessed by QUIC.cloud employees in any way, except as necessary in providing maintenance or technical support. QUIC.cloud keeps copies of that data for up to 7 days and then permanently deletes them. Similarly, the WordPress plugin has a Reporting feature whereby a site owner can transmit an environment report to LiteSpeed so that we may better provide technical support. None of these features collects any visitor data. Only server and site data are involved. +In addition to caching, our WordPress plugin has online features provided by QUIC.cloud for Image Optimization and Page Optimization services. When one of these optimizations is requested, data is transmitted to a remote QUIC.cloud server, processed, and then transmitted back for use on your site. QUIC.cloud keeps copies of that data for up to 7 days and then permanently deletes it. Similarly, the WordPress plugin has a Reporting feature whereby a site owner can transmit an environment report to LiteSpeed so that we may better provide technical support. None of these features collects any visitor data. Only server and site data are involved. + +QUIC.cloud CDN, if enabled, uses LSCache technology to access your site, and serve your content from remote global nodes. Your data is not accessed by QUIC.cloud employees in any way, except as necessary in providing maintenance or technical support. Please see the [QUIC.cloud Privacy Policy](https://quic.cloud/privacy-policy/) for our complete Privacy/GDPR statement. @@ -255,18 +257,73 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == -= 7.3 - Aug 2025 = += 7.6 - Oct 22 2025 = +* **VPI** Add fetchpriority and decoding attributes to VPI. (Hirak Kalita, serpentdriver, PR#903) +* **API** Dropped legacy `conf::val()` function. + += 7.5.0.1 - Sep 11 2025 = +* 🐞**GUI** Fixed an issue where the network dashboard template was missing. (mcworks) + += 7.5 - Sep 10 2025 = +* 🌱**Image Optimize** New option `Optimize Image Sizes` to allow user to choose which image sizes to include in optimization request. +* 🐞**Purge** Purge Time setting will respect WP timezone setting now. (PR#893) +* 🐞**Conf** Fixed a minor type-casting bug, which could cause unnecessary QUIC.cloud sync configuration when the setting is empty. +* **Misc** Dropped unused rewrite rule from htaccess. + += 7.4 - Aug 28 2025 = +* 🌱**Media** Added new Auto Rescale Original Image option. +* 🌱**Toolbox** Added ability to Disable All for 24 Hours. (PR#886) +* 🐞**CDN** Fixed a QUIC.cloud sync configuration failure on network child sites. +* 🐞**Object Cache** Fixed a bug that failed to detect the Redis connection status. +* **Cache** Better match iPhone browsers for mobile cache detection. +* **Cache** Dropped use of `advanced-cache.php` support since WP v5.3+ doesn't need it, and LiteSpeed requires WP v5.3+. +* **Cache** When page is not cacheable, set header to value used by WordPress `Cache-Control` header. (asafm7) +* **Page Optimize** Better compatibility for dummy CSS removal in cases where other plugins manipulate the quotation marks. +* **Page Optimize** Dropped v4.2 legacy `LITESPEED_BYPASS_OPTM`. +* **Crawler** Now use an .html file to test the port, as some security plugins block .txt files and cause port test failure. (#661828) +* **GUI** Show current live values for options if they are overridden by filters or the server environment. (PR#885) +* **Data** Dropped legacy code and upgraded data migration support to LSCWP v5.7-. +* **Misc** Support the `LITESPEED_DEV` constant to allow switching to a development environment. +* **Misc** Allow leading underscore (`_`) for private functions and variables in format checker. +* **Misc** Suppress frequent version check when a certain database option is cached. +* **Misc** Dropped `sanitize_file_name` usage to prevent template failure when 3rd party plugins manipulate that filter. + += 7.3.0.1 - Jul 30 2025 = +* **Page Optimize** Fixed the page score impact caused by CSS placeholder. (wpconvert, Sean Thompson) +* **Page Optimize** Fixed wrong prefetch/preload injection when a page contains other `` tags. (idatahuy) +* **Crawler** Bypassed port test if no server IP set. (kptk, serkanix, Guillermo) + += 7.3 - Jul 24 2025 = +* 🌱**CLI** Added `wp litespeed-database` database optimization command. +* 🌱**Misc** Added survey and data deletion reminder in deactivation process. * **Core** Refactored the template files to comply with WordPress standards. -* **ESI** Fixed an Edit button missing case on frontend when the permalink structure is `Plain`. (#934261 PR#860) -* **API** Added filter `litespeed_purge_tags` to allow manipulation of purge tags. +* **Core** Refactored the CLI files to comply with WordPress standards. Fixed a bug with CLI `option` command failure handler. +* **ESI** Fixed a case where the Edit button is missing on the frontend when the permalink structure is `Plain`. (#934261 PR#860) +* **API** Added `litespeed_purge_tags` filter to allow manipulation of purge tags. * **API** Allowed overriding `litespeed_ui_events` via window property. (Zsombor Franczia PR#865) -* **Debug** Allowed debug in multisite network level. (PR#861) -* **Vary** Fixed possible duplicate webp vary in chrome mimicked iPhone visit. -* 🐞**Vary** Used simpler rewrite rule to check next gen image format support. -* 🐞**Page Optimize** Added the missing JS Delay lib when page optimization is off while iframe lazyload is on. (Zsombor Franczia #867) -* **GUI** Added admin bar icon to Enable All Features when disabled all. (Tobolo, PR#868) -* **Misc** Simplified admin JS. -* **Misc** Added existing plugin version to ping API for debug purpose. +* **API** Added `litespeed_vpi_should_queue` filter to allow control over appending to the VPI queue. (tompalmer #855, Viktor Szépe PR#856) +* **Debug** Allowed debug at multisite network level. (PR#861) +* **Vary** Fixed a possible duplicate WebP vary in Chrome when mimicking an iPhone visit. +* 🐞**Vary** Used simpler rewrite rule to check for next generation image format support. +* **Page Optimize** Tuned the optimized data injection location in HTML to improve SEO. (videofinanzas) +* **Page Optimize** Improved DNS prefetch and preconnect sequence in HTML to be as early as possible. Simplified DNS optimization code. +* 🐞**Page Optimize** Added the JS Delay library that was missing when page optimization was off while iframe lazy load was on. (Zsombor Franczia #867) +* 🐞**Page Optimize** Allowed lazy load threshold overwrite. (Zsombor Franczia #852 PR#857) +* 🐞**Page Optimize** Fixed an issue where the `async` attribute was replaced even when it contained a value, e.g. `async=true`. (@macorak) +* 🐞**Cloud** Fixed the API call timestamp file creation warning. +* **Cloud** No longer include public key when logging QUIC.cloud registration process. +* **Image Optimize** Resend all images that failed to pull instead of bypassing them. (Ryan D) +* **Crawler** Checked QUIC.cloud CDN for crawler hit. (PR#866) +* 🐞**Crawler** Fixed an issue where the non-role-simulator crawler added the whole map to the blocklist on servers that only support port 80. +* **GUI** Added Enable All Features icon to admin bar when all features are disabled. This replaces the banner that previously displayed in admin. (Tobolo, PR#868) +* **GUI** Dropped font files. (Masoud Najjar Khodabakhsh) +* **3rd** Resolved an issue with an empty WooCommerce ESI nonce and HTML comments on geolocation redirection. (#612331 PR#708) +* **OPcache** Detected `opcache.restrict_api` setting to prevent PHP warning in purge. (ookris #9496550 PR#812) +* **Misc** Simplified admin JavaScript. +* **Misc** Fixed download import file extension issue on mobile. (autori76 #874) +* **Misc** Added existing plugin version to ping API for debugging purposes. +* **Misc** Fixed comment typos reported by static analysis. (Viktor Szépe PR#836) +* **Misc** Removed global variables from plugin initialization file. (Viktor Szépe PR#837) = 7.2 - Jun 18 2025 = * 🌱**CDN** New option: Cloudflare Clear on purge all. (PR#828) diff --git a/src/activation.cls.php b/src/activation.cls.php index 9c52644ff..bc89ea3e4 100644 --- a/src/activation.cls.php +++ b/src/activation.cls.php @@ -1,5 +1,4 @@ <?php - /** * The plugin activation class. * @@ -12,8 +11,15 @@ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Activation + * + * Handles plugin activation, deactivation, and related file management. + * + * @since 1.1.0 + */ class Activation extends Base { const TYPE_UPGRADE = 'upgrade'; @@ -23,115 +29,142 @@ class Activation extends Base { const NETWORK_TRANSIENT_COUNT = 'lscwp_network_count'; - private static $_data_file; + /** + * Data file path for configuration. + * + * @since 4.1 + * @var string + */ + private static $data_file; /** * Construct * + * Initializes the data file path. + * * @since 4.1 */ public function __construct() { - self::$_data_file = LSCWP_CONTENT_DIR . '/' . self::CONF_FILE; + self::$data_file = LSCWP_CONTENT_DIR . '/' . self::CONF_FILE; } /** * The activation hook callback. * + * Handles plugin activation tasks, including file creation and multisite setup. + * * @since 1.0.0 * @access public */ public static function register_activation() { - global $wp_version; - $advanced_cache = LSCWP_CONTENT_DIR . '/advanced-cache.php'; - if (version_compare($wp_version, '5.3', '<') && !file_exists($advanced_cache)) { - $file_pointer = fopen($advanced_cache, 'w'); - fwrite($file_pointer, "<?php\n\n// A compatibility placeholder for WordPress < v5.3\n// Created by LSCWP v6.1+"); - fclose($file_pointer); - } - $count = 0; - !defined('LSCWP_LOG_TAG') && define('LSCWP_LOG_TAG', 'Activate_' . get_current_blog_id()); + ! defined( 'LSCWP_LOG_TAG' ) && define( 'LSCWP_LOG_TAG', 'Activate_' . get_current_blog_id() ); /* Network file handler */ - if (is_multisite()) { + if ( is_multisite() ) { $count = self::get_network_count(); - if ($count !== false) { - $count = intval($count) + 1; - set_site_transient(self::NETWORK_TRANSIENT_COUNT, $count, DAY_IN_SECONDS); + if ( false !== $count ) { + $count = (int) $count + 1; + set_site_transient( self::NETWORK_TRANSIENT_COUNT, $count, DAY_IN_SECONDS ); } - if (!is_network_admin()) { - if ($count === 1) { + if ( ! is_network_admin() ) { + if ( 1 === $count ) { // Only itself is activated, set .htaccess with only CacheLookUp try { Htaccess::cls()->insert_ls_wrapper(); - } catch (\Exception $ex) { - Admin_Display::error($ex->getMessage()); + } catch ( \Exception $ex ) { + Admin_Display::error( $ex->getMessage() ); } } } } self::cls()->update_files(); - if (defined('LSCWP_REF') && LSCWP_REF == 'whm') { - GUI::update_option(GUI::WHM_MSG, GUI::WHM_MSG_VAL); + if ( defined( 'LSCWP_REF' ) && 'whm' === LSCWP_REF ) { + GUI::update_option( GUI::WHM_MSG, GUI::WHM_MSG_VAL ); } } /** * Uninstall plugin * + * Removes all LiteSpeed Cache settings and data. + * * @since 1.1.0 + * @since 7.3 Updated to remove all settings. + * @access public */ public static function uninstall_litespeed_cache() { Task::destroy(); - // Delete options - foreach (Conf::cls()->load_default_vals() as $k => $v) { - Base::delete_option($k); - } + if ( is_multisite() ) { + // Save main site id + $current_blog = get_current_blog_id(); + + // get all sites + $sub_sites = get_sites(); + + // clear foreach site + foreach ( $sub_sites as $sub_site ) { + $sub_blog_id = (int) $sub_site->blog_id; + if ( $sub_blog_id !== $current_blog ) { + // Switch to blog + switch_to_blog( $sub_blog_id ); - // Delete site options - if (is_multisite()) { - foreach (Conf::cls()->load_default_site_vals() as $k => $v) { - Base::delete_site_option($k); + // Delete site options + self::delete_settings(); + + // Delete site tables + Data::cls()->tables_del(); + } } + + // Return to main site + switch_to_blog( $current_blog ); } - // Delete avatar table + // Delete current blog/site + // Delete options + self::delete_settings(); + + // Delete site tables Data::cls()->tables_del(); - if (file_exists(LITESPEED_STATIC_DIR)) { - File::rrmdir(LITESPEED_STATIC_DIR); + if ( file_exists( LITESPEED_STATIC_DIR ) ) { + File::rrmdir( LITESPEED_STATIC_DIR ); } - Cloud::version_check('uninstall'); + Cloud::version_check( 'uninstall' ); + } - // Files has been deleted when deactivated + /** + * Remove all litespeed settings. + * + * Deletes all LiteSpeed Cache options from the database. + * + * @since 7.3 + * @access private + */ + private static function delete_settings() { + global $wpdb; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $wpdb->query($wpdb->prepare("DELETE FROM `$wpdb->options` WHERE option_name LIKE %s", 'litespeed.%')); } /** * Get the blog ids for the network. Accepts function arguments. * - * Will use wp_get_sites for WP versions less than 4.6 - * * @since 1.0.12 * @access public + * @param array $args Arguments for get_sites(). * @return array The array of blog ids. */ public static function get_network_ids( $args = array() ) { - global $wp_version; - if (version_compare($wp_version, '4.6', '<')) { - $blogs = wp_get_sites($args); - if (!empty($blogs)) { - foreach ($blogs as $key => $blog) { - $blogs[$key] = $blog['blog_id']; - } - } - } else { - $args['fields'] = 'ids'; - $blogs = get_sites($args); - } + $args['fields'] = 'ids'; + $blogs = get_sites( $args ); + return $blogs; } @@ -140,25 +173,26 @@ public static function get_network_ids( $args = array() ) { * * @since 1.0.12 * @access private + * @return int|false Number of active LSCWP or false if none. */ private static function get_network_count() { - $count = get_site_transient(self::NETWORK_TRANSIENT_COUNT); - if ($count !== false) { - return intval($count); + $count = get_site_transient( self::NETWORK_TRANSIENT_COUNT ); + if ( false !== $count ) { + return (int) $count; } // need to update $default = array(); $count = 0; - $sites = self::get_network_ids(array( 'deleted' => 0 )); - if (empty($sites)) { + $sites = self::get_network_ids( array( 'deleted' => 0 ) ); + if ( empty( $sites ) ) { return false; } - foreach ($sites as $site) { - $bid = is_object($site) && property_exists($site, 'blog_id') ? $site->blog_id : $site; - $plugins = get_blog_option($bid, 'active_plugins', $default); - if (!empty($plugins) && in_array(LSCWP_BASENAME, $plugins, true)) { + foreach ( $sites as $site ) { + $bid = is_object( $site ) && property_exists( $site, 'blog_id' ) ? $site->blog_id : $site; + $plugins = get_blog_option( $bid, 'active_plugins', $default ); + if ( ! empty( $plugins ) && in_array( LSCWP_BASENAME, $plugins, true ) ) { ++$count; } } @@ -169,11 +203,11 @@ private static function get_network_count() { * @see https://codex.wordpress.org/Function_Reference/is_plugin_active_for_network * @since 2.0 */ - if (!function_exists('is_plugin_active_for_network')) { + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { require_once ABSPATH . '/wp-admin/includes/plugin.php'; } - if (is_plugin_active_for_network(LSCWP_BASENAME)) { + if ( is_plugin_active_for_network( LSCWP_BASENAME ) ) { ++$count; } return $count; @@ -187,17 +221,17 @@ private static function get_network_count() { */ private static function is_deactivate_last() { $count = self::get_network_count(); - if ($count === false) { + if ( false === $count ) { return false; } - if ($count !== 1) { + if ( 1 !== $count ) { // Not deactivating the last one. --$count; - set_site_transient(self::NETWORK_TRANSIENT_COUNT, $count, DAY_IN_SECONDS); + set_site_transient( self::NETWORK_TRANSIENT_COUNT, $count, DAY_IN_SECONDS ); return false; } - delete_site_transient(self::NETWORK_TRANSIENT_COUNT); + delete_site_transient( self::NETWORK_TRANSIENT_COUNT ); return true; } @@ -212,17 +246,17 @@ private static function is_deactivate_last() { public static function register_deactivation() { Task::destroy(); - !defined('LSCWP_LOG_TAG') && define('LSCWP_LOG_TAG', 'Deactivate_' . get_current_blog_id()); + ! defined( 'LSCWP_LOG_TAG' ) && define( 'LSCWP_LOG_TAG', 'Deactivate_' . get_current_blog_id() ); Purge::purge_all(); - if (is_multisite()) { - if (!self::is_deactivate_last()) { - if (is_network_admin()) { + if ( is_multisite() ) { + if ( ! self::is_deactivate_last() ) { + if ( is_network_admin() ) { // Still other activated subsite left, set .htaccess with only CacheLookUp try { Htaccess::cls()->insert_ls_wrapper(); - } catch (\Exception $ex) { + } catch ( \Exception $ex ) { Admin_Display::error($ex->getMessage()); } } @@ -233,11 +267,12 @@ public static function register_deactivation() { /* 1) wp-config.php; */ try { - self::cls()->_manage_wp_cache_const(false); - } catch (\Exception $ex) { - error_log('In wp-config.php: WP_CACHE could not be set to false during deactivation!'); + self::cls()->manage_wp_cache_const( false ); + } catch ( \Exception $ex ) { + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.PHP.DevelopmentFunctions.error_log_error_log + error_log( 'In wp-config.php: WP_CACHE could not be set to false during deactivation!' ); - Admin_Display::error($ex->getMessage()); + Admin_Display::error( $ex->getMessage() ); } /* 2) adv-cache.php; Dropped in v3.0.4 */ @@ -250,13 +285,15 @@ public static function register_deactivation() { try { Htaccess::cls()->clear_rules(); - } catch (\Exception $ex) { - Admin_Display::error($ex->getMessage()); + } catch ( \Exception $ex ) { + Admin_Display::error( $ex->getMessage() ); } /* 5) .litespeed_conf.dat; */ - self::_del_conf_data_file(); + self::del_conf_data_file(); + + /* 6) delete option lscwp_whm_install */ // delete in case it's not deleted prior to deactivation. GUI::dismiss_whm(); @@ -265,8 +302,6 @@ public static function register_deactivation() { /** * Manage related files based on plugin latest conf * - * NOTE: Only trigger this in backend admin access for efficiency concern - * * Handle files: * 1) wp-config.php; * 2) adv-cache.php; @@ -278,10 +313,10 @@ public static function register_deactivation() { * @access public */ public function update_files() { - Debug2::debug('🗂️ [Activation] update_files'); + Debug2::debug( '🗂️ [Activation] update_files' ); // Update cache setting `_CACHE` - $this->cls('Conf')->define_cache(); + $this->cls( 'Conf' )->define_cache(); // Site options applied already $options = $this->get_options(); @@ -289,56 +324,70 @@ public function update_files() { /* 1) wp-config.php; */ try { - $this->_manage_wp_cache_const($options[self::_CACHE]); - } catch (\Exception $ex) { + $this->manage_wp_cache_const( $options[ self::_CACHE ] ); + } catch ( \Exception $ex ) { // Add msg to admin page or CLI - Admin_Display::error($ex->getMessage()); + Admin_Display::error( wp_kses_post( $ex->getMessage() ) ); } /* 2) adv-cache.php; Dropped in v3.0.4 */ /* 3) object-cache.php; */ - if ($options[self::O_OBJECT] && (!$options[self::O_DEBUG_DISABLE_ALL] || is_multisite())) { - $this->cls('Object_Cache')->update_file($options); + if ( $options[ self::O_OBJECT ] && ( ! $options[ self::O_DEBUG_DISABLE_ALL ] || is_multisite() ) ) { + $this->cls( 'Object_Cache' )->update_file( $options ); } else { - $this->cls('Object_Cache')->del_file(); // Note: because it doesn't reconnect, which caused setting page OC option changes delayed, thus may meet Connect Test Failed issue (Next refresh will correct it). Not a big deal, will keep as is. + $this->cls( 'Object_Cache' )->del_file(); // Note: because it doesn't reconnect, which caused setting page OC option changes delayed, thus may meet Connect Test Failed issue (Next refresh will correct it). Not a big deal, will keep as is. } /* 4) .htaccess; */ try { - $this->cls('Htaccess')->update($options); - } catch (\Exception $ex) { - Admin_Display::error($ex->getMessage()); + $this->cls( 'Htaccess' )->update( $options ); + } catch ( \Exception $ex ) { + Admin_Display::error( wp_kses_post( $ex->getMessage() ) ); } /* 5) .litespeed_conf.dat; */ - if (($options[self::O_GUEST] || $options[self::O_OBJECT]) && (!$options[self::O_DEBUG_DISABLE_ALL] || is_multisite())) { - $this->_update_conf_data_file($options); + if ( ( $options[ self::O_GUEST ] || $options[ self::O_OBJECT ] ) && ( ! $options[ self::O_DEBUG_DISABLE_ALL ] || is_multisite() ) ) { + $this->update_conf_data_file( $options ); } } /** * Delete data conf file * + * Removes the .litespeed_conf.dat file. + * * @since 4.1 + * @access private */ - private static function _del_conf_data_file() { - if (file_exists(self::$_data_file)) { - unlink(self::$_data_file); + private static function del_conf_data_file() { + global $wp_filesystem; + + if ( ! $wp_filesystem ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + WP_Filesystem(); + } + + if ( $wp_filesystem->exists( self::$data_file ) ) { + $wp_filesystem->delete( self::$data_file ); } } /** * Update data conf file for guest mode & object cache * + * Updates the .litespeed_conf.dat file with relevant settings. + * * @since 4.1 + * @access private + * @param array $options Plugin options. */ - private function _update_conf_data_file( $options ) { + private function update_conf_data_file( $options ) { $ids = array(); - if ($options[self::O_OBJECT]) { + if ( $options[ self::O_OBJECT ] ) { $this_ids = array( self::O_DEBUG, self::O_OBJECT_KIND, @@ -354,24 +403,31 @@ private function _update_conf_data_file( $options ) { self::O_OBJECT_GLOBAL_GROUPS, self::O_OBJECT_NON_PERSISTENT_GROUPS, ); - $ids = array_merge($ids, $this_ids); + $ids = array_merge( $ids, $this_ids ); } - if ($options[self::O_GUEST]) { - $this_ids = array( self::HASH, self::O_CACHE_LOGIN_COOKIE, self::O_DEBUG_IPS, self::O_UTIL_NO_HTTPS_VARY, self::O_GUEST_UAS, self::O_GUEST_IPS ); - $ids = array_merge($ids, $this_ids); + if ( $options[ self::O_GUEST ] ) { + $this_ids = array( + self::HASH, + self::O_CACHE_LOGIN_COOKIE, + self::O_DEBUG_IPS, + self::O_UTIL_NO_HTTPS_VARY, + self::O_GUEST_UAS, + self::O_GUEST_IPS, + ); + $ids = array_merge( $ids, $this_ids ); } $data = array(); - foreach ($ids as $v) { - $data[$v] = $options[$v]; + foreach ( $ids as $v ) { + $data[ $v ] = $options[ $v ]; } - $data = \json_encode($data); + $data = wp_json_encode( $data ); - $old_data = File::read(self::$_data_file); - if ($old_data != $data) { - defined('LSCWP_LOG') && Debug2::debug('[Activation] Updating .litespeed_conf.dat'); - File::save(self::$_data_file, $data); + $old_data = File::read( self::$data_file ); + if ( $old_data !== $data ) { + defined( 'LSCWP_LOG' ) && Debug2::debug( '[Activation] Updating .litespeed_conf.dat' ); + File::save( self::$data_file, $data ); } } @@ -383,19 +439,21 @@ private function _update_conf_data_file( $options ) { * * @since 1.0.0 * @since 3.0 Refactored - * @access private + * @param bool $enable Whether to enable WP_CACHE. + * @throws \Exception If wp-config.php cannot be modified. + * @return bool True if updated, false if no change needed. */ - private function _manage_wp_cache_const( $enable ) { - if ($enable) { - if (defined('WP_CACHE') && WP_CACHE) { + public function manage_wp_cache_const( $enable ) { + if ( $enable ) { + if ( defined( 'WP_CACHE' ) && WP_CACHE ) { return false; } - } elseif (!defined('WP_CACHE') || (defined('WP_CACHE') && !WP_CACHE)) { + } elseif ( ! defined( 'WP_CACHE' ) || ( defined( 'WP_CACHE' ) && ! WP_CACHE ) ) { return false; } - if (apply_filters('litespeed_wpconfig_readonly', false)) { - throw new \Exception('wp-config file is forbidden to modify due to API hook: litespeed_wpconfig_readonly'); + if ( apply_filters( 'litespeed_wpconfig_readonly', false ) ) { + throw new \Exception( 'wp-config file is forbidden to modify due to API hook: litespeed_wpconfig_readonly' ); } /** @@ -404,29 +462,29 @@ private function _manage_wp_cache_const( $enable ) { * @see wp-load.php */ $conf_file = ABSPATH . 'wp-config.php'; - if (!file_exists($conf_file)) { - $conf_file = dirname(ABSPATH) . '/wp-config.php'; + if ( ! file_exists( $conf_file ) ) { + $conf_file = dirname( ABSPATH ) . '/wp-config.php'; } - $content = File::read($conf_file); - if (!$content) { - throw new \Exception('wp-config file content is empty: ' . $conf_file); + $content = File::read( $conf_file ); + if ( ! $content ) { + throw new \Exception( 'wp-config file content is empty: ' . wp_kses_post( $conf_file ) ); } // Remove the line `define('WP_CACHE', true/false);` first - if (defined('WP_CACHE')) { - $content = preg_replace('/define\(\s*([\'"])WP_CACHE\1\s*,\s*\w+\s*\)\s*;/sU', '', $content); + if ( defined( 'WP_CACHE' ) ) { + $content = preg_replace( '/define\(\s*([\'"])WP_CACHE\1\s*,\s*\w+\s*\)\s*;/sU', '', $content ); } // Insert const - if ($enable) { - $content = preg_replace('/^<\?php/', "<?php\ndefine( 'WP_CACHE', true );", $content); + if ( $enable ) { + $content = preg_replace( '/^<\?php/', "<?php\ndefine( 'WP_CACHE', true );", $content ); } - $res = File::save($conf_file, $content, false, false, false); + $res = File::save( $conf_file, $content, false, false, false ); - if ($res !== true) { - throw new \Exception('wp-config.php operation failed when changing `WP_CACHE` const: ' . $res); + if ( true !== $res ) { + throw new \Exception( 'wp-config.php operation failed when changing `WP_CACHE` const: ' . wp_kses_post( $res ) ); } return true; @@ -435,28 +493,35 @@ private function _manage_wp_cache_const( $enable ) { /** * Handle auto update * + * Enables auto-updates for the plugin if configured. + * * @since 2.7.2 * @access public */ public function auto_update() { - if (!$this->conf(Base::O_AUTO_UPGRADE)) { + if ( ! $this->conf( Base::O_AUTO_UPGRADE ) ) { return; } - add_filter('auto_update_plugin', array( $this, 'auto_update_hook' ), 10, 2); + add_filter( 'auto_update_plugin', array( $this, 'auto_update_hook' ), 10, 2 ); } /** * Auto upgrade hook * + * Determines whether to auto-update the plugin. + * * @since 3.0 * @access public + * @param bool $update Whether to update. + * @param object $item Plugin data. + * @return bool Whether to update. */ public function auto_update_hook( $update, $item ) { - if (!empty($item->slug) && 'litespeed-cache' === $item->slug) { - $auto_v = Cloud::version_check('auto_update_plugin'); + if ( ! empty( $item->slug ) && 'litespeed-cache' === $item->slug ) { + $auto_v = Cloud::version_check( 'auto_update_plugin' ); - if (!empty($auto_v['latest']) && !empty($item->new_version) && $auto_v['latest'] === $item->new_version) { + if ( ! empty( $auto_v['latest'] ) && ! empty( $item->new_version ) && $auto_v['latest'] === $item->new_version ) { return true; } } @@ -467,6 +532,8 @@ public function auto_update_hook( $update, $item ) { /** * Upgrade LSCWP * + * Upgrades the LiteSpeed Cache plugin. + * * @since 2.9 * @access public */ @@ -474,6 +541,8 @@ public function upgrade() { $plugin = Core::PLUGIN_FILE; /** + * Load upgrade cls + * * @see wp-admin/update.php */ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; @@ -483,64 +552,73 @@ public function upgrade() { try { ob_start(); $skin = new \WP_Ajax_Upgrader_Skin(); - $upgrader = new \Plugin_Upgrader($skin); - $result = $upgrader->upgrade($plugin); - if (!is_plugin_active($plugin)) { + $upgrader = new \Plugin_Upgrader( $skin ); + $result = $upgrader->upgrade( $plugin ); + if ( ! is_plugin_active( $plugin ) ) { // todo: upgrade should reactivate the plugin again by WP. Need to check why disabled after upgraded. - activate_plugin($plugin, '', is_multisite()); + activate_plugin( $plugin, '', is_multisite() ); } ob_end_clean(); - } catch (\Exception $e) { - Admin_Display::error(__('Failed to upgrade.', 'litespeed-cache')); + } catch ( \Exception $e ) { + Admin_Display::error( __( 'Failed to upgrade.', 'litespeed-cache' ) ); return; } - if (is_wp_error($result)) { - Admin_Display::error(__('Failed to upgrade.', 'litespeed-cache')); + if ( is_wp_error( $result ) ) { + Admin_Display::error( __( 'Failed to upgrade.', 'litespeed-cache' ) ); return; } - Admin_Display::success(__('Upgraded successfully.', 'litespeed-cache')); + Admin_Display::success( __( 'Upgraded successfully.', 'litespeed-cache' ) ); } /** * Detect if the plugin is active or not * * @since 1.0 + * @access public + * @param string $plugin Plugin slug. + * @return bool True if active, false otherwise. */ public function dash_notifier_is_plugin_active( $plugin ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; $plugin_path = $plugin . '/' . $plugin . '.php'; - return is_plugin_active($plugin_path); + return is_plugin_active( $plugin_path ); } /** * Detect if the plugin is installed or not * * @since 1.0 + * @access public + * @param string $plugin Plugin slug. + * @return bool True if installed, false otherwise. */ public function dash_notifier_is_plugin_installed( $plugin ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; $plugin_path = $plugin . '/' . $plugin . '.php'; - $valid = validate_plugin($plugin_path); + $valid = validate_plugin( $plugin_path ); - return !is_wp_error($valid); + return ! is_wp_error( $valid ); } /** * Grab a plugin info from WordPress * * @since 1.0 + * @access public + * @param string $slug Plugin slug. + * @return object|false Plugin info or false on failure. */ public function dash_notifier_get_plugin_info( $slug ) { include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - $result = plugins_api('plugin_information', array( 'slug' => $slug )); + $result = plugins_api( 'plugin_information', array( 'slug' => $slug ) ); - if (is_wp_error($result)) { + if ( is_wp_error( $result ) ) { return false; } @@ -550,19 +628,25 @@ public function dash_notifier_get_plugin_info( $slug ) { /** * Install the 3rd party plugin * + * Installs and activates a third-party plugin. + * * @since 1.0 + * @access public */ public function dash_notifier_install_3rd() { - !defined('SILENCE_INSTALL') && define('SILENCE_INSTALL', true); + ! defined( 'SILENCE_INSTALL' ) && define( 'SILENCE_INSTALL', true ); - $slug = !empty($_GET['plugin']) ? $_GET['plugin'] : false; + // phpcs:ignore + $slug = ! empty( $_GET['plugin'] ) ? wp_unslash( sanitize_text_field( $_GET['plugin'] ) ) : false; // Check if plugin is installed already - if (!$slug || $this->dash_notifier_is_plugin_active($slug)) { + if ( ! $slug || $this->dash_notifier_is_plugin_active( $slug ) ) { return; } /** + * Load upgrade cls + * * @see wp-admin/update.php */ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; @@ -571,58 +655,60 @@ public function dash_notifier_install_3rd() { $plugin_path = $slug . '/' . $slug . '.php'; - if (!$this->dash_notifier_is_plugin_installed($slug)) { - $plugin_info = $this->dash_notifier_get_plugin_info($slug); - if (!$plugin_info) { + if ( ! $this->dash_notifier_is_plugin_installed( $slug ) ) { + $plugin_info = $this->dash_notifier_get_plugin_info( $slug ); + if ( ! $plugin_info ) { return; } // Try to install plugin try { ob_start(); $skin = new \Automatic_Upgrader_Skin(); - $upgrader = new \Plugin_Upgrader($skin); - $result = $upgrader->install($plugin_info->download_link); + $upgrader = new \Plugin_Upgrader( $skin ); + $result = $upgrader->install( $plugin_info->download_link ); ob_end_clean(); - } catch (\Exception $e) { + } catch ( \Exception $e ) { return; } } - if (!is_plugin_active($plugin_path)) { - activate_plugin($plugin_path); + if ( ! is_plugin_active( $plugin_path ) ) { + activate_plugin( $plugin_path ); } } /** * Handle all request actions from main cls * + * Processes various activation-related actions. + * * @since 2.9 * @access public */ public function handler() { $type = Router::verify_type(); - switch ($type) { + switch ( $type ) { case self::TYPE_UPGRADE: - $this->upgrade(); + $this->upgrade(); break; case self::TYPE_INSTALL_3RD: - $this->dash_notifier_install_3rd(); + $this->dash_notifier_install_3rd(); break; case self::TYPE_DISMISS_RECOMMENDED: - Cloud::reload_summary(); - Cloud::save_summary(array( 'news.new' => 0 )); + Cloud::reload_summary(); + Cloud::save_summary( array( 'news.new' => 0 ) ); break; case self::TYPE_INSTALL_ZIP: - Cloud::reload_summary(); - $summary = Cloud::get_summary(); - if (!empty($summary['news.zip'])) { - Cloud::save_summary(array( 'news.new' => 0 )); + Cloud::reload_summary(); + $summary = Cloud::get_summary(); + if ( ! empty( $summary['news.zip'] ) ) { + Cloud::save_summary( array( 'news.new' => 0 ) ); - $this->cls('Debug2')->beta_test($summary['zip']); + $this->cls( 'Debug2' )->beta_test( $summary['zip'] ); } break; diff --git a/src/admin-display.cls.php b/src/admin-display.cls.php index e5e999a8f..fc8fd227e 100644 --- a/src/admin-display.cls.php +++ b/src/admin-display.cls.php @@ -1,8 +1,10 @@ <?php - /** * The admin-panel specific functionality of the plugin. * + * Provides admin page rendering, notices, enqueueing of assets, + * menu registrations, and various admin utilities. + * * @since 1.0.0 * @package LiteSpeed * @subpackage LiteSpeed/admin @@ -11,447 +13,742 @@ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Admin_Display + * + * Handles WP-Admin UI for LiteSpeed Cache. + */ class Admin_Display extends Base { + /** + * Log tag for Admin_Display. + * + * @var string + */ const LOG_TAG = '👮‍♀️'; - const NOTICE_BLUE = 'notice notice-info'; - const NOTICE_GREEN = 'notice notice-success'; - const NOTICE_RED = 'notice notice-error'; + /** + * Notice class (info/blue). + * + * @var string + */ + const NOTICE_BLUE = 'notice notice-info'; + /** + * Notice class (success/green). + * + * @var string + */ + const NOTICE_GREEN = 'notice notice-success'; + /** + * Notice class (error/red). + * + * @var string + */ + const NOTICE_RED = 'notice notice-error'; + /** + * Notice class (warning/yellow). + * + * @var string + */ const NOTICE_YELLOW = 'notice notice-warning'; - const DB_MSG = 'messages'; - const DB_MSG_PIN = 'msg_pin'; + /** + * Option key for one-time messages. + * + * @var string + */ + const DB_MSG = 'messages'; + /** + * Option key for pinned messages. + * + * @var string + */ + const DB_MSG_PIN = 'msg_pin'; + /** + * Purge by: category. + * + * @var string + */ const PURGEBY_CAT = '0'; + /** + * Purge by: post ID. + * + * @var string + */ const PURGEBY_PID = '1'; + /** + * Purge by: tag. + * + * @var string + */ const PURGEBY_TAG = '2'; + /** + * Purge by: URL. + * + * @var string + */ const PURGEBY_URL = '3'; + /** + * Purge selection field name. + * + * @var string + */ const PURGEBYOPT_SELECT = 'purgeby'; - const PURGEBYOPT_LIST = 'purgebylist'; + /** + * Purge list field name. + * + * @var string + */ + const PURGEBYOPT_LIST = 'purgebylist'; - const DB_DISMISS_MSG = 'dismiss'; - const RULECONFLICT_ON = 'ExpiresDefault_1'; + /** + * Dismiss key for messages. + * + * @var string + */ + const DB_DISMISS_MSG = 'dismiss'; + /** + * Rule conflict flag (on). + * + * @var string + */ + const RULECONFLICT_ON = 'ExpiresDefault_1'; + /** + * Rule conflict dismissed flag. + * + * @var string + */ const RULECONFLICT_DISMISSED = 'ExpiresDefault_0'; - const TYPE_QC_HIDE_BANNER = 'qc_hide_banner'; + /** + * Router type for QC hide banner. + * + * @var string + */ + const TYPE_QC_HIDE_BANNER = 'qc_hide_banner'; + /** + * Cookie name for QC hide banner. + * + * @var string + */ const COOKIE_QC_HIDE_BANNER = 'litespeed_qc_hide_banner'; - protected $messages = array(); - protected $default_settings = array(); + /** + * Internal messages cache. + * + * @var array<string,string> + */ + protected $messages = array(); + + /** + * Cached default settings. + * + * @var array<string,mixed> + */ + protected $default_settings = array(); + + /** + * Whether current context is network admin. + * + * @var bool + */ protected $_is_network_admin = false; - protected $_is_multisite = false; + /** + * Whether multisite is enabled. + * + * @var bool + */ + protected $_is_multisite = false; + + /** + * Incremental form submit button index. + * + * @var int + */ private $_btn_i = 0; + /** + * List of settings with filters and return type. + * + * @since 7.4 + * + * @var array<string,array<string,mixed>> + */ + protected static $settings_filters = [ + // Crawler - Blocklist. + 'crawler-blocklist' => [ + 'filter' => 'litespeed_crawler_disable_blocklist', + 'type' => 'boolean', + ], + // Crawler - Settings. + self::O_CRAWLER_LOAD_LIMIT => [ + 'filter' => [ Base::ENV_CRAWLER_LOAD_LIMIT_ENFORCE, Base::ENV_CRAWLER_LOAD_LIMIT ], + 'type' => 'input', + ], + // Cache - ESI. + self::O_ESI_NONCE => [ + 'filter' => 'litespeed_esi_nonces', + ], + // Page Optimization - CSS. + 'optm-ucss_per_pagetype' => [ + 'filter' => 'litespeed_ucss_per_pagetype', + 'type' => 'boolean', + ], + // Page Optimization - Media. + self::O_MEDIA_ADD_MISSING_SIZES => [ + 'filter' => 'litespeed_media_ignore_remote_missing_sizes', + 'type' => 'boolean', + ], + // Page Optimization - Media Exclude. + self::O_MEDIA_LAZY_EXC => [ + 'filter' => 'litespeed_media_lazy_img_excludes', + ], + // Page Optimization - Tuning (JS). + self::O_OPTM_JS_DELAY_INC => [ + 'filter' => 'litespeed_optm_js_delay_inc', + ], + self::O_OPTM_JS_EXC => [ + 'filter' => 'litespeed_optimize_js_excludes', + ], + self::O_OPTM_JS_DEFER_EXC => [ + 'filter' => 'litespeed_optm_js_defer_exc', + ], + self::O_OPTM_GM_JS_EXC => [ + 'filter' => 'litespeed_optm_gm_js_exc', + ], + self::O_OPTM_EXC => [ + 'filter' => 'litespeed_optm_uri_exc', + ], + // Page Optimization - Tuning (CSS). + self::O_OPTM_CSS_EXC => [ + 'filter' => 'litespeed_optimize_css_excludes', + ], + self::O_OPTM_UCSS_EXC => [ + 'filter' => 'litespeed_ucss_exc', + ], + ]; + + /** + * Flat pages map: menu slug to template metadata. + * + * @var array<string,array{title:string,tpl:string,network?:bool}> + */ + private $_pages = []; + /** * Initialize the class and set its properties. * - * @since 1.0.7 + * @since 1.0.7 */ public function __construct() { + $this->_pages = [ + // Site-level pages + 'litespeed' => [ 'title' => __( 'Dashboard', 'litespeed-cache' ), 'tpl' => 'dash/entry.tpl.php' ], + 'litespeed-optimax' => [ 'title' => __( 'OptimaX', 'litespeed-cache' ), 'tpl' => 'optimax/entry.tpl.php', 'scope' => 'site' ], + 'litespeed-presets' => [ 'title' => __( 'Presets', 'litespeed-cache' ), 'tpl' => 'presets/entry.tpl.php', 'scope' => 'site' ], + 'litespeed-general' => [ 'title' => __( 'General', 'litespeed-cache' ), 'tpl' => 'general/entry.tpl.php' ], + 'litespeed-cache' => [ 'title' => __( 'Cache', 'litespeed-cache' ), 'tpl' => 'cache/entry.tpl.php' ], + 'litespeed-cdn' => [ 'title' => __( 'CDN', 'litespeed-cache' ), 'tpl' => 'cdn/entry.tpl.php', 'scope' => 'site' ], + 'litespeed-img_optm' => [ 'title' => __( 'Image Optimization', 'litespeed-cache'), 'tpl' => 'img_optm/entry.tpl.php' ], + 'litespeed-page_optm' => [ 'title' => __( 'Page Optimization', 'litespeed-cache' ), 'tpl' => 'page_optm/entry.tpl.php', 'scope' => 'site' ], + 'litespeed-db_optm' => [ 'title' => __( 'Database', 'litespeed-cache' ), 'tpl' => 'db_optm/entry.tpl.php' ], + 'litespeed-crawler' => [ 'title' => __( 'Crawler', 'litespeed-cache' ), 'tpl' => 'crawler/entry.tpl.php', 'scope' => 'site' ], + 'litespeed-toolbox' => [ 'title' => __( 'Toolbox', 'litespeed-cache' ), 'tpl' => 'toolbox/entry.tpl.php' ], + ]; + // main css - add_action('admin_enqueue_scripts', array( $this, 'enqueue_style' )); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_style' ) ); // Main js - add_action('admin_enqueue_scripts', array( $this, 'enqueue_scripts' )); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); $this->_is_network_admin = is_network_admin(); $this->_is_multisite = is_multisite(); // Quick access menu - if (is_multisite() && $this->_is_network_admin) { - $manage = 'manage_network_options'; - } else { - $manage = 'manage_options'; - } - if (current_user_can($manage)) { - add_action('wp_before_admin_bar_render', array( GUI::cls(), 'backend_shortcut' )); + $manage = ( $this->_is_multisite && $this->_is_network_admin ) ? 'manage_network_options' : 'manage_options'; - // `admin_notices` is after `admin_enqueue_scripts` - // @see wp-admin/admin-header.php - add_action($this->_is_network_admin ? 'network_admin_notices' : 'admin_notices', array( $this, 'display_messages' )); + if ( current_user_can( $manage ) ) { + add_action( 'wp_before_admin_bar_render', array( GUI::cls(), 'backend_shortcut' ) ); + + // `admin_notices` is after `admin_enqueue_scripts`. + add_action( $this->_is_network_admin ? 'network_admin_notices' : 'admin_notices', array( $this, 'display_messages' ) ); } /** - * In case this is called outside the admin page + * In case this is called outside the admin page. * * @see https://codex.wordpress.org/Function_Reference/is_plugin_active_for_network * @since 2.0 */ - if (!function_exists('is_plugin_active_for_network')) { - require_once ABSPATH . '/wp-admin/includes/plugin.php'; + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; } - // add menus ( Also check for mu-plugins) - if ($this->_is_network_admin && (is_plugin_active_for_network(LSCWP_BASENAME) || defined('LSCWP_MU_PLUGIN'))) { - add_action('network_admin_menu', array( $this, 'register_admin_menu' )); + // add menus (Also check for mu-plugins) + if ( $this->_is_network_admin && ( is_plugin_active_for_network( LSCWP_BASENAME ) || defined( 'LSCWP_MU_PLUGIN' ) ) ) { + add_action( 'network_admin_menu', array( $this, 'register_admin_menu' ) ); } else { - add_action('admin_menu', array( $this, 'register_admin_menu' )); + add_action( 'admin_menu', array( $this, 'register_admin_menu' ) ); } - $this->cls('Metabox')->register_settings(); + $this->cls( 'Metabox' )->register_settings(); } /** - * Show the title of one line + * Echo a translated section title. + * + * @since 3.0 * - * @since 3.0 - * @access public + * @param string $id Language key. + * @return void */ public function title( $id ) { - echo Lang::title($id); + echo wp_kses_post( Lang::title( $id ) ); } /** - * Register the admin menu display. + * Bind per-page admin hooks for a given page hook. * - * @since 1.0.0 - * @access public + * Adds footer text filter and preview banner when loading the page. + * + * @param string $hook Page hook suffix returned by add_*_page(). + * @return void */ - public function register_admin_menu() { - $capability = $this->_is_network_admin ? 'manage_network_options' : 'manage_options'; - if (current_user_can($capability)) { - // root menu - add_menu_page('LiteSpeed Cache', 'LiteSpeed Cache', 'manage_options', 'litespeed'); - - // sub menus - $this->_add_submenu(__('Dashboard', 'litespeed-cache'), 'litespeed', 'show_menu_dash'); - - !$this->_is_network_admin && $this->_add_submenu(__('Presets', 'litespeed-cache'), 'litespeed-presets', 'show_menu_presets'); - - $this->_add_submenu(__('General', 'litespeed-cache'), 'litespeed-general', 'show_menu_general'); - - $this->_add_submenu(__('Cache', 'litespeed-cache'), 'litespeed-cache', 'show_menu_cache'); - - !$this->_is_network_admin && $this->_add_submenu(__('CDN', 'litespeed-cache'), 'litespeed-cdn', 'show_menu_cdn'); - - $this->_add_submenu(__('Image Optimization', 'litespeed-cache'), 'litespeed-img_optm', 'show_img_optm'); - - !$this->_is_network_admin && $this->_add_submenu(__('Page Optimization', 'litespeed-cache'), 'litespeed-page_optm', 'show_page_optm'); - - $this->_add_submenu(__('Database', 'litespeed-cache'), 'litespeed-db_optm', 'show_db_optm'); - - !$this->_is_network_admin && $this->_add_submenu(__('Crawler', 'litespeed-cache'), 'litespeed-crawler', 'show_crawler'); - - $this->_add_submenu(__('Toolbox', 'litespeed-cache'), 'litespeed-toolbox', 'show_toolbox'); + private function bind_page( $hook ) { + add_action( "load-$hook", function () { + add_filter( + 'admin_footer_text', + function ( $footer_text ) { + $this->cls( 'Cloud' )->maybe_preview_banner(); + require_once LSCWP_DIR . 'tpl/inc/admin_footer.php'; + return $footer_text; + }, + 1 + ); + } ); + } - // sub menus under options - add_options_page('LiteSpeed Cache', 'LiteSpeed Cache', $capability, 'litespeed-cache-options', array( $this, 'show_menu_cache' )); - } + /** + * Render an admin page by slug using its mapped template file. + * + * @param string $slug The menu slug registered in $_pages. + * @return void + */ + private function render_page( $slug ) { + $tpl = LSCWP_DIR . 'tpl/' . $this->_pages[ $slug ]['tpl']; + is_file( $tpl ) ? require $tpl : wp_die( 'Template not found' ); } /** - * Helper function to set up a submenu page. + * Register the admin menu display. * - * @since 1.0.4 - * @access private - * @param string $menu_title The title that appears on the menu. - * @param string $menu_slug The slug of the page. - * @param string $callback The callback to call if selected. + * @since 1.0.0 + * @return void */ - private function _add_submenu( $menu_title, $menu_slug, $callback ) { - add_submenu_page('litespeed', $menu_title, $menu_title, 'manage_options', $menu_slug, array( $this, $callback )); + public function register_admin_menu() { + $capability = $this->_is_network_admin ? 'manage_network_options' : 'manage_options'; + $scope = $this->_is_network_admin ? 'network' : 'site'; + + add_menu_page( + 'LiteSpeed Cache', + 'LiteSpeed Cache', + $capability, + 'litespeed', + ); + + foreach ( $this->_pages as $slug => $meta ) { + if ( 'litespeed-optimax' === $slug && !defined( 'LITESPEED_OX' ) ) { + continue; + } + if ( ! empty( $meta['scope'] ) && $meta['scope'] !== $scope ) { + continue; + } + $hook = add_submenu_page( + 'litespeed', + $meta['title'], + $meta['title'], + $capability, + $slug, + function () use ( $slug ) { + $this->render_page( $slug ); + } + ); + $this->bind_page( $hook ); + } + + // sub menus under options. + $hook = add_options_page( + 'LiteSpeed Cache', + 'LiteSpeed Cache', + $capability, + 'litespeed-cache-options', + function () { + $this->render_page( 'litespeed-cache' ); + } + ); + $this->bind_page( $hook ); } /** * Register the stylesheets for the admin area. * - * @since 1.0.14 - * @access public + * @since 1.0.14 + * @return void */ public function enqueue_style() { - wp_enqueue_style(Core::PLUGIN_NAME, LSWCP_PLUGIN_URL . 'assets/css/litespeed.css', array(), Core::VER, 'all'); + wp_enqueue_style( Core::PLUGIN_NAME, LSWCP_PLUGIN_URL . 'assets/css/litespeed.css', array(), Core::VER, 'all' ); } /** - * Register the JavaScript for the admin area. + * Register/enqueue the JavaScript for the admin area. * - * @since 1.0.0 - * @access public + * @since 1.0.0 + * @since 7.3 Added deactivation modal code. + * @return void */ public function enqueue_scripts() { - wp_register_script(Core::PLUGIN_NAME, LSWCP_PLUGIN_URL . 'assets/js/litespeed-cache-admin.js', array(), Core::VER, false); + wp_register_script( Core::PLUGIN_NAME, LSWCP_PLUGIN_URL . 'assets/js/litespeed-cache-admin.js', array(), Core::VER, false ); $localize_data = array(); - if (GUI::has_whm_msg()) { - $ajax_url_dismiss_whm = Utility::build_url(Core::ACTION_DISMISS, GUI::TYPE_DISMISS_WHM, true); + if ( GUI::has_whm_msg() ) { + $ajax_url_dismiss_whm = Utility::build_url( Core::ACTION_DISMISS, GUI::TYPE_DISMISS_WHM, true ); $localize_data['ajax_url_dismiss_whm'] = $ajax_url_dismiss_whm; } - if (GUI::has_msg_ruleconflict()) { - $ajax_url = Utility::build_url(Core::ACTION_DISMISS, GUI::TYPE_DISMISS_EXPIRESDEFAULT, true); + if ( GUI::has_msg_ruleconflict() ) { + $ajax_url = Utility::build_url( Core::ACTION_DISMISS, GUI::TYPE_DISMISS_EXPIRESDEFAULT, true ); $localize_data['ajax_url_dismiss_ruleconflict'] = $ajax_url; } // Injection to LiteSpeed pages global $pagenow; - if ($pagenow == 'admin.php' && !empty($_GET['page']) && (strpos($_GET['page'], 'litespeed-') === 0 || $_GET['page'] == 'litespeed')) { - // Admin footer - add_filter('admin_footer_text', array( $this, 'admin_footer_text' ), 1); + $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - if ($_GET['page'] == 'litespeed-crawler' || $_GET['page'] == 'litespeed-cdn') { + if ( 'admin.php' === $pagenow && $page && ( 0 === strpos( $page, 'litespeed-' ) || 'litespeed' === $page ) ) { + if ( in_array( $page, array( 'litespeed-crawler', 'litespeed-cdn' ), true ) ) { // Babel JS type correction - add_filter('script_loader_tag', array( $this, 'babel_type' ), 10, 3); + add_filter( 'script_loader_tag', array( $this, 'babel_type' ), 10, 3 ); - wp_enqueue_script(Core::PLUGIN_NAME . '-lib-react', LSWCP_PLUGIN_URL . 'assets/js/react.min.js', array(), Core::VER, false); - wp_enqueue_script(Core::PLUGIN_NAME . '-lib-babel', LSWCP_PLUGIN_URL . 'assets/js/babel.min.js', array(), Core::VER, false); + wp_enqueue_script( Core::PLUGIN_NAME . '-lib-react', LSWCP_PLUGIN_URL . 'assets/js/react.min.js', array(), Core::VER, false ); + wp_enqueue_script( Core::PLUGIN_NAME . '-lib-babel', LSWCP_PLUGIN_URL . 'assets/js/babel.min.js', array(), Core::VER, false ); } // Crawler Cookie Simulation - if ($_GET['page'] == 'litespeed-crawler') { - wp_enqueue_script(Core::PLUGIN_NAME . '-crawler', LSWCP_PLUGIN_URL . 'assets/js/component.crawler.js', array(), Core::VER, false); + if ( 'litespeed-crawler' === $page ) { + wp_enqueue_script( Core::PLUGIN_NAME . '-crawler', LSWCP_PLUGIN_URL . 'assets/js/component.crawler.js', array(), Core::VER, false ); + $localize_data['lang'] = array(); - $localize_data['lang']['cookie_name'] = __('Cookie Name', 'litespeed-cache'); - $localize_data['lang']['cookie_value'] = __('Cookie Values', 'litespeed-cache'); - $localize_data['lang']['one_per_line'] = Doc::one_per_line(true); - $localize_data['lang']['remove_cookie_simulation'] = __('Remove cookie simulation', 'litespeed-cache'); - $localize_data['lang']['add_cookie_simulation_row'] = __('Add new cookie to simulate', 'litespeed-cache'); - empty($localize_data['ids']) && ($localize_data['ids'] = array()); + $localize_data['lang']['cookie_name'] = __( 'Cookie Name', 'litespeed-cache' ); + $localize_data['lang']['cookie_value'] = __( 'Cookie Values', 'litespeed-cache' ); + $localize_data['lang']['one_per_line'] = Doc::one_per_line( true ); + $localize_data['lang']['remove_cookie_simulation'] = __( 'Remove cookie simulation', 'litespeed-cache' ); + $localize_data['lang']['add_cookie_simulation_row'] = __( 'Add new cookie to simulate', 'litespeed-cache' ); + if ( empty( $localize_data['ids'] ) ) { + $localize_data['ids'] = array(); + } $localize_data['ids']['crawler_cookies'] = self::O_CRAWLER_COOKIES; } // CDN mapping - if ($_GET['page'] == 'litespeed-cdn') { - $home_url = home_url('/'); - $parsed = parse_url($home_url); - $home_url = str_replace($parsed['scheme'] . ':', '', $home_url); - $cdn_url = 'https://cdn.' . substr($home_url, 2); + if ( 'litespeed-cdn' === $page ) { + $home_url = home_url( '/' ); + $parsed = wp_parse_url( $home_url ); + if ( ! empty( $parsed['scheme'] ) ) { + $home_url = str_replace( $parsed['scheme'] . ':', '', $home_url ); + } + $cdn_url = 'https://cdn.' . substr( $home_url, 2 ); - wp_enqueue_script(Core::PLUGIN_NAME . '-cdn', LSWCP_PLUGIN_URL . 'assets/js/component.cdn.js', array(), Core::VER, false); + wp_enqueue_script( Core::PLUGIN_NAME . '-cdn', LSWCP_PLUGIN_URL . 'assets/js/component.cdn.js', array(), Core::VER, false ); $localize_data['lang'] = array(); - $localize_data['lang']['cdn_mapping_url'] = Lang::title(self::CDN_MAPPING_URL); - $localize_data['lang']['cdn_mapping_inc_img'] = Lang::title(self::CDN_MAPPING_INC_IMG); - $localize_data['lang']['cdn_mapping_inc_css'] = Lang::title(self::CDN_MAPPING_INC_CSS); - $localize_data['lang']['cdn_mapping_inc_js'] = Lang::title(self::CDN_MAPPING_INC_JS); - $localize_data['lang']['cdn_mapping_filetype'] = Lang::title(self::CDN_MAPPING_FILETYPE); - $localize_data['lang']['cdn_mapping_url_desc'] = sprintf(__('CDN URL to be used. For example, %s', 'litespeed-cache'), '<code>' . $cdn_url . '</code>'); - $localize_data['lang']['one_per_line'] = Doc::one_per_line(true); - $localize_data['lang']['cdn_mapping_remove'] = __('Remove CDN URL', 'litespeed-cache'); - $localize_data['lang']['add_cdn_mapping_row'] = __('Add new CDN URL', 'litespeed-cache'); - $localize_data['lang']['on'] = __('ON', 'litespeed-cache'); - $localize_data['lang']['off'] = __('OFF', 'litespeed-cache'); - empty($localize_data['ids']) && ($localize_data['ids'] = array()); + $localize_data['lang']['cdn_mapping_url'] = Lang::title( self::CDN_MAPPING_URL ); + $localize_data['lang']['cdn_mapping_inc_img'] = Lang::title( self::CDN_MAPPING_INC_IMG ); + $localize_data['lang']['cdn_mapping_inc_css'] = Lang::title( self::CDN_MAPPING_INC_CSS ); + $localize_data['lang']['cdn_mapping_inc_js'] = Lang::title( self::CDN_MAPPING_INC_JS ); + $localize_data['lang']['cdn_mapping_filetype'] = Lang::title( self::CDN_MAPPING_FILETYPE ); + $localize_data['lang']['cdn_mapping_url_desc'] = sprintf( __( 'CDN URL to be used. For example, %s', 'litespeed-cache' ), '<code>' . esc_html( $cdn_url ) . '</code>' ); + $localize_data['lang']['one_per_line'] = Doc::one_per_line( true ); + $localize_data['lang']['cdn_mapping_remove'] = __( 'Remove CDN URL', 'litespeed-cache' ); + $localize_data['lang']['add_cdn_mapping_row'] = __( 'Add new CDN URL', 'litespeed-cache' ); + $localize_data['lang']['on'] = __( 'ON', 'litespeed-cache' ); + $localize_data['lang']['off'] = __( 'OFF', 'litespeed-cache' ); + if ( empty( $localize_data['ids'] ) ) { + $localize_data['ids'] = array(); + } $localize_data['ids']['cdn_mapping'] = self::O_CDN_MAPPING; } } - if ($localize_data) { - wp_localize_script(Core::PLUGIN_NAME, 'litespeed_data', $localize_data); + // Load iziModal JS and CSS + $show_deactivation_modal = ( is_multisite() && ! is_network_admin() ) ? false : true; + if ( $show_deactivation_modal && 'plugins.php' === $pagenow ) { + wp_enqueue_script( Core::PLUGIN_NAME . '-iziModal', LSWCP_PLUGIN_URL . 'assets/js/iziModal.min.js', array(), Core::VER, true ); + wp_enqueue_style( Core::PLUGIN_NAME . '-iziModal', LSWCP_PLUGIN_URL . 'assets/css/iziModal.min.css', array(), Core::VER, 'all' ); + add_action( 'admin_footer', array( $this, 'add_deactivation_html' ) ); } - wp_enqueue_script(Core::PLUGIN_NAME); + if ( $localize_data ) { + wp_localize_script( Core::PLUGIN_NAME, 'litespeed_data', $localize_data ); + } + + wp_enqueue_script( Core::PLUGIN_NAME ); } /** - * Babel type for crawler + * Add modal HTML on Plugins screen. * - * @since 3.6 + * @since 7.3 + * @return void + */ + public function add_deactivation_html() { + require LSCWP_DIR . 'tpl/inc/modal.deactivation.php'; + } + + /** + * Filter the script tag for specific handles to set Babel type. + * + * @since 3.6 + * + * @param string $tag The script tag. + * @param string $handle Script handle. + * @param string $src Script source URL. + * @return string The filtered script tag. */ public function babel_type( $tag, $handle, $src ) { - if ($handle != Core::PLUGIN_NAME . '-crawler' && $handle != Core::PLUGIN_NAME . '-cdn') { + if ( Core::PLUGIN_NAME . '-crawler' !== $handle && Core::PLUGIN_NAME . '-cdn' !== $handle ) { return $tag; } - return '<script src="' . Str::trim_quotes($src) . '" type="text/babel"></script>'; + // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript + return '<script src="' . Str::trim_quotes( $src ) . '" type="text/babel"></script>'; } /** * Callback that adds LiteSpeed Cache's action links. * * @since 1.0.0 - * @access public - * @param array $links Previously added links from other plugins. - * @return array Links array with the litespeed cache one appended. + * + * @param array<string> $links Previously added links from other plugins. + * @return array<string> Links with the LiteSpeed Cache one appended. */ public function add_plugin_links( $links ) { - // $links[] = '<a href="' . admin_url('options-general.php?page=litespeed-cache') . '">' . __('Settings', 'litespeed-cache') . '</a>'; - $links[] = '<a href="' . admin_url('admin.php?page=litespeed-cache') . '">' . __('Settings', 'litespeed-cache') . '</a>'; + $links[] = '<a href="' . esc_url( admin_url( 'admin.php?page=litespeed-cache' ) ) . '">' . esc_html__( 'Settings', 'litespeed-cache' ) . '</a>'; return $links; } /** - * Change the admin footer text on LiteSpeed Cache admin pages. - * - * @since 1.0.13 - * @param string $footer_text - * @return string - */ - public function admin_footer_text( $footer_text ) { - require_once LSCWP_DIR . 'tpl/inc/admin_footer.php'; - - return $footer_text; - } - - /** - * Builds the html for a single notice. + * Build a single notice HTML string. * * @since 1.0.7 - * @access public - * @param string $color The color to use for the notice. - * @param string $str The notice message. - * @return string The built notice html. + * + * @param string $color The color CSS class for the notice. + * @param string $str The notice message. + * @param bool $irremovable If true, the notice cannot be dismissed. + * @param string $additional_classes Additional classes to add to the wrapper. + * @return string The built notice HTML. */ public static function build_notice( $color, $str, $irremovable = false, $additional_classes = '' ) { $cls = $color; - if ($irremovable) { + if ( $irremovable ) { $cls .= ' litespeed-irremovable'; } else { $cls .= ' is-dismissible'; } - if ($additional_classes) { + if ( $additional_classes ) { $cls .= ' ' . $additional_classes; } // possible translation - $str = Lang::maybe_translate($str); + $str = Lang::maybe_translate( $str ); - return '<div class="litespeed_icon ' . $cls . '"><p>' . wp_kses_post($str) . '</p></div>'; + return '<div class="litespeed_icon ' . esc_attr( $cls ) . '"><p>' . wp_kses_post( $str ) . '</p></div>'; } /** - * Display info notice + * Display info notice. * * @since 1.6.5 - * @access public + * + * @param string|array<string> $msg Message or list of messages. + * @param bool $do_echo Echo immediately instead of storing. + * @param bool $irremovable If true, cannot be dismissed. + * @param string $additional_classes Extra CSS classes. + * @return void */ - public static function info( $msg, $echo = false, $irremovable = false, $additional_classes = '' ) { - self::add_notice(self::NOTICE_BLUE, $msg, $echo, $irremovable, $additional_classes); + public static function info( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) { + self::add_notice( self::NOTICE_BLUE, $msg, $do_echo, $irremovable, $additional_classes ); } /** - * Display note notice + * Display note (warning) notice. * * @since 1.6.5 - * @access public + * + * @param string|array<string> $msg Message or list of messages. + * @param bool $do_echo Echo immediately instead of storing. + * @param bool $irremovable If true, cannot be dismissed. + * @param string $additional_classes Extra CSS classes. + * @return void */ - public static function note( $msg, $echo = false, $irremovable = false, $additional_classes = '' ) { - self::add_notice(self::NOTICE_YELLOW, $msg, $echo, $irremovable, $additional_classes); + public static function note( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) { + self::add_notice( self::NOTICE_YELLOW, $msg, $do_echo, $irremovable, $additional_classes ); } /** - * Display success notice + * Display success notice. * * @since 1.6 - * @access public + * + * @param string|array<string> $msg Message or list of messages. + * @param bool $do_echo Echo immediately instead of storing. + * @param bool $irremovable If true, cannot be dismissed. + * @param string $additional_classes Extra CSS classes. + * @return void */ - public static function success( $msg, $echo = false, $irremovable = false, $additional_classes = '' ) { - self::add_notice(self::NOTICE_GREEN, $msg, $echo, $irremovable, $additional_classes); + public static function success( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) { + self::add_notice( self::NOTICE_GREEN, $msg, $do_echo, $irremovable, $additional_classes ); } - /** @deprecated 4.7 */ - /** will drop in v7.5 */ - public static function succeed( $msg, $echo = false, $irremovable = false, $additional_classes = '' ) { - self::success($msg, $echo, $irremovable, $additional_classes); + + /** + * Deprecated alias for success(). + * + * @deprecated 4.7 Will drop in v7.5. Use success(). + * + * @param string|array<string> $msg Message or list of messages. + * @param bool $do_echo Echo immediately instead of storing. + * @param bool $irremovable If true, cannot be dismissed. + * @param string $additional_classes Extra CSS classes. + * @return void + */ + public static function succeed( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) { + self::success( $msg, $do_echo, $irremovable, $additional_classes ); } /** - * Display error notice + * Display error notice. * * @since 1.6 - * @access public + * + * @param string|array<string> $msg Message or list of messages. + * @param bool $do_echo Echo immediately instead of storing. + * @param bool $irremovable If true, cannot be dismissed. + * @param string $additional_classes Extra CSS classes. + * @return void */ - public static function error( $msg, $echo = false, $irremovable = false, $additional_classes = '' ) { - self::add_notice(self::NOTICE_RED, $msg, $echo, $irremovable, $additional_classes); + public static function error( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) { + self::add_notice( self::NOTICE_RED, $msg, $do_echo, $irremovable, $additional_classes ); } /** - * Add irremovable msg + * Add unique (irremovable optional) messages. * * @since 4.7 + * + * @param string $color_mode One of info|note|success|error. + * @param string|array<string> $msgs Message(s). + * @param bool $irremovable If true, cannot be dismissed. + * @return void */ public static function add_unique_notice( $color_mode, $msgs, $irremovable = false ) { - if (!is_array($msgs)) { + if ( ! is_array( $msgs ) ) { $msgs = array( $msgs ); } $color_map = array( - 'info' => self::NOTICE_BLUE, - 'note' => self::NOTICE_YELLOW, + 'info' => self::NOTICE_BLUE, + 'note' => self::NOTICE_YELLOW, 'success' => self::NOTICE_GREEN, - 'error' => self::NOTICE_RED, + 'error' => self::NOTICE_RED, ); - if (empty($color_map[$color_mode])) { - self::debug('Wrong admin display color mode!'); + if ( empty( $color_map[ $color_mode ] ) ) { + self::debug( 'Wrong admin display color mode!' ); return; } - $color = $color_map[$color_mode]; + $color = $color_map[ $color_mode ]; - // Go through to make sure unique + // Go through to make sure unique. $filtered_msgs = array(); - foreach ($msgs as $k => $str) { - if (is_numeric($k)) { - $k = md5($str); - } // Use key to make it overwritable to previous same msg - $filtered_msgs[$k] = $str; + foreach ( $msgs as $k => $str ) { + if ( is_numeric( $k ) ) { + $k = md5( $str ); + } // Use key to make it overwritable to previous same msg. + $filtered_msgs[ $k ] = $str; } - self::add_notice($color, $filtered_msgs, false, $irremovable); + self::add_notice( $color, $filtered_msgs, false, $irremovable ); } /** - * Adds a notice to display on the admin page + * Add a notice to display on the admin page (store or echo). * * @since 1.0.7 - * @access public + * + * @param string $color Notice color CSS class. + * @param string|array<string> $msg Message(s). + * @param bool $do_echo Echo immediately instead of storing. + * @param bool $irremovable If true, cannot be dismissed. + * @param string $additional_classes Extra classes for wrapper. + * @return void */ - public static function add_notice( $color, $msg, $echo = false, $irremovable = false, $additional_classes = '' ) { - // self::debug("add_notice msg", $msg); + public static function add_notice( $color, $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) { // Bypass adding for CLI or cron - if (defined('LITESPEED_CLI') || wp_doing_cron()) { + if ( defined( 'LITESPEED_CLI' ) || wp_doing_cron() ) { // WP CLI will show the info directly - if (defined('WP_CLI') && WP_CLI) { - if (!is_array($msg)) { + if ( defined( 'WP_CLI' ) && constant('WP_CLI') ) { + if ( ! is_array( $msg ) ) { $msg = array( $msg ); } - foreach ($msg as $v) { - $v = strip_tags($v); - if ($color == self::NOTICE_RED) { - \WP_CLI::error($v, false); + foreach ( $msg as $v ) { + $v = wp_strip_all_tags( $v ); + if ( self::NOTICE_RED === $color ) { + \WP_CLI::error( $v, false ); } else { - \WP_CLI::success($v); + \WP_CLI::success( $v ); } } } return; } - if ($echo) { - echo self::build_notice($color, $msg, $irremovable, $additional_classes); + if ( $do_echo ) { + echo self::build_notice( $color, $msg, $irremovable, $additional_classes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return; } $msg_name = $irremovable ? self::DB_MSG_PIN : self::DB_MSG; - $messages = self::get_option($msg_name, array()); - if (!is_array($messages)) { + $messages = self::get_option( $msg_name, array() ); + if ( ! is_array( $messages ) ) { $messages = array(); } - if (is_array($msg)) { - foreach ($msg as $k => $str) { - $messages[$k] = self::build_notice($color, $str, $irremovable, $additional_classes); + if ( is_array( $msg ) ) { + foreach ( $msg as $k => $str ) { + $messages[ $k ] = self::build_notice( $color, $str, $irremovable, $additional_classes ); } } else { - $messages[] = self::build_notice($color, $msg, $irremovable, $additional_classes); + $messages[] = self::build_notice( $color, $msg, $irremovable, $additional_classes ); } - $messages = array_unique($messages); - self::update_option($msg_name, $messages); + $messages = array_unique( $messages ); + self::update_option( $msg_name, $messages ); } /** - * Display notices and errors in dashboard + * Display notices and errors in dashboard. * * @since 1.1.0 - * @access public + * @return void */ public function display_messages() { - if (!defined('LITESPEED_CONF_LOADED')) { + if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) { $this->_in_upgrading(); } - if (GUI::has_whm_msg()) { + if ( GUI::has_whm_msg() ) { $this->show_display_installed(); } @@ -461,65 +758,56 @@ public function display_messages() { Cloud::cls()->check_dev_version(); // One time msg - $messages = self::get_option(self::DB_MSG, array()); + $messages = self::get_option( self::DB_MSG, array() ); $added_thickbox = false; - if (is_array($messages)) { - foreach ($messages as $msg) { + if ( is_array( $messages ) ) { + foreach ( $messages as $msg ) { // Added for popup links - if (strpos($msg, 'TB_iframe') && !$added_thickbox) { + if ( strpos( $msg, 'TB_iframe' ) && ! $added_thickbox ) { add_thickbox(); $added_thickbox = true; } - echo wp_kses_post($msg); + echo wp_kses_post( $msg ); } } - if ($messages != -1) { - self::update_option(self::DB_MSG, -1); + if ( -1 !== $messages ) { + self::update_option( self::DB_MSG, -1 ); } // Pinned msg - $messages = self::get_option(self::DB_MSG_PIN, array()); - if (is_array($messages)) { - foreach ($messages as $k => $msg) { + $messages = self::get_option( self::DB_MSG_PIN, array() ); + if ( is_array( $messages ) ) { + foreach ( $messages as $k => $msg ) { // Added for popup links - if (strpos($msg, 'TB_iframe') && !$added_thickbox) { + if ( strpos( $msg, 'TB_iframe' ) && ! $added_thickbox ) { add_thickbox(); $added_thickbox = true; } // Append close btn - if (substr($msg, -6) == '</div>') { - $link = Utility::build_url(Core::ACTION_DISMISS, GUI::TYPE_DISMISS_PIN, false, null, array( 'msgid' => $k )); + if ( '</div>' === substr( $msg, -6 ) ) { + $link = Utility::build_url( Core::ACTION_DISMISS, GUI::TYPE_DISMISS_PIN, false, null, array( 'msgid' => $k ) ); $msg = - substr($msg, 0, -6) . + substr( $msg, 0, -6 ) . '<p><a href="' . - $link . + esc_url( $link ) . '" class="button litespeed-btn-primary litespeed-btn-mini">' . - __('Dismiss', 'litespeed-cache') . + esc_html__( 'Dismiss', 'litespeed-cache' ) . '</a>' . '</p></div>'; } - echo wp_kses_post($msg); + echo wp_kses_post( $msg ); } } - // if ( $messages != -1 ) { - // self::update_option( self::DB_MSG_PIN, -1 ); - // } - // Show disable all warning - if (defined('LITESPEED_DISABLE_ALL') && LITESPEED_DISABLE_ALL) { - self::error(Error::msg('disabled_all'), true); - } - - if (empty($_GET['page']) || strpos($_GET['page'], 'litespeed') !== 0) { + if ( empty( $_GET['page'] ) || 0 !== strpos( sanitize_text_field( wp_unslash( $_GET['page'] ) ), 'litespeed' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended global $pagenow; - if ($pagenow != 'plugins.php') { - // && $pagenow != 'index.php' + if ( 'plugins.php' !== $pagenow ) { return; } } - if (!$this->conf(self::O_NEWS)) { + if ( ! $this->conf( self::O_NEWS ) ) { return; } @@ -538,55 +826,61 @@ public function display_messages() { } /** - * Dismiss pinned msg + * Dismiss pinned msg. * * @since 3.5.2 - * @access public + * @return void */ public static function dismiss_pin() { - if (!isset($_GET['msgid'])) { + if ( ! isset( $_GET['msgid'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } - $messages = self::get_option(self::DB_MSG_PIN, array()); - if (!is_array($messages) || empty($messages[$_GET['msgid']])) { + $messages = self::get_option( self::DB_MSG_PIN, array() ); + $msgid = sanitize_text_field( wp_unslash( $_GET['msgid'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + if ( ! is_array( $messages ) || empty( $messages[ $msgid ] ) ) { return; } - unset($messages[$_GET['msgid']]); - if (!$messages) { + unset( $messages[ $msgid ] ); + if ( ! $messages ) { $messages = -1; } - self::update_option(self::DB_MSG_PIN, $messages); + self::update_option( self::DB_MSG_PIN, $messages ); } /** - * Dismiss pinned msg by msg content + * Dismiss pinned msg by msg content. * * @since 7.0 - * @access public + * + * @param string $content Message content. + * @param string $color Color CSS class. + * @param bool $irremovable Is irremovable. + * @return void */ public static function dismiss_pin_by_content( $content, $color, $irremovable ) { - $content = self::build_notice($color, $content, $irremovable); - $messages = self::get_option(self::DB_MSG_PIN, array()); + $content = self::build_notice( $color, $content, $irremovable ); + $messages = self::get_option( self::DB_MSG_PIN, array() ); $hit = false; - if ($messages != -1) { - foreach ($messages as $k => $v) { - if ($v == $content) { - unset($messages[$k]); + if ( -1 !== $messages ) { + foreach ( $messages as $k => $v ) { + if ( $v === $content ) { + unset( $messages[ $k ] ); $hit = true; - self::debug('✅ pinned msg content hit. Removed'); + self::debug( '✅ pinned msg content hit. Removed' ); break; } } } - if ($hit) { - if (!$messages) { + if ( $hit ) { + if ( ! $messages ) { $messages = -1; } - self::update_option(self::DB_MSG_PIN, $messages); + self::update_option( self::DB_MSG_PIN, $messages ); } else { - self::debug('❌ No pinned msg content hit'); + self::debug( '❌ No pinned msg content hit' ); } } @@ -596,129 +890,21 @@ public static function dismiss_pin_by_content( $content, $color, $irremovable ) * This will append the esi on/off selector and ttl text. * * @since 1.1.0 - * @access public - */ - public function show_widget_edit( $widget, $return, $instance ) { - require LSCWP_DIR . 'tpl/esi_widget_edit.php'; - } - - /** - * Displays the dashboard page. * - * @since 3.0 - * @access public - */ - public function show_menu_dash() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/dash/entry.tpl.php'; - } - - /** - * Displays the General page. - * - * @since 5.3 - * @access public - */ - public function show_menu_presets() { - require_once LSCWP_DIR . 'tpl/presets/entry.tpl.php'; - } - - /** - * Displays the General page. - * - * @since 3.0 - * @access public - */ - public function show_menu_general() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/general/entry.tpl.php'; - } - - /** - * Displays the CDN page. - * - * @since 3.0 - * @access public - */ - public function show_menu_cdn() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/cdn/entry.tpl.php'; - } - - /** - * Outputs the LiteSpeed Cache settings page. - * - * @since 1.0.0 - * @access public - */ - public function show_menu_cache() { - if ($this->_is_network_admin) { - require_once LSCWP_DIR . 'tpl/cache/entry_network.tpl.php'; - } else { - require_once LSCWP_DIR . 'tpl/cache/entry.tpl.php'; - } - } - - /** - * Tools page - * - * @since 3.0 - * @access public - */ - public function show_toolbox() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/toolbox/entry.tpl.php'; - } - - /** - * Outputs the crawler operation page. - * - * @since 1.1.0 - * @access public - */ - public function show_crawler() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/crawler/entry.tpl.php'; - } - - /** - * Outputs the optimization operation page. - * - * @since 1.6 - * @access public - */ - public function show_img_optm() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/img_optm/entry.tpl.php'; - } - - /** - * Page optm page. - * - * @since 3.0 - * @access public - */ - public function show_page_optm() { - $this->cls('Cloud')->maybe_preview_banner(); - require_once LSCWP_DIR . 'tpl/page_optm/entry.tpl.php'; - } - - /** - * DB optm page. - * - * @since 3.0 - * @access public + * @param \WP_Widget $widget The widget instance (passed by reference). + * @param mixed $return_val Return param (unused). + * @param array $instance The widget instance's settings. + * @return void */ - public function show_db_optm() { - require_once LSCWP_DIR . 'tpl/db_optm/entry.tpl.php'; + public function show_widget_edit( $widget, $return_val, $instance ) { + require LSCWP_DIR . 'tpl/esi_widget_edit.php'; } /** - * Outputs a notice to the admin panel when the plugin is installed - * via the WHM plugin. + * Outputs a notice when the plugin is installed via WHM. * * @since 1.0.12 - * @access public + * @return void */ public function show_display_installed() { require_once LSCWP_DIR . 'tpl/inc/show_display_installed.php'; @@ -728,516 +914,663 @@ public function show_display_installed() { * Display error cookie msg. * * @since 1.0.12 - * @access public + * @return void */ public static function show_error_cookie() { require_once LSCWP_DIR . 'tpl/inc/show_error_cookie.php'; } /** - * Display warning if lscache is disabled + * Display warning if lscache is disabled. * * @since 2.1 - * @access public + * @return void */ public function cache_disabled_warning() { include LSCWP_DIR . 'tpl/inc/check_cache_disabled.php'; } /** - * Display conf data upgrading banner + * Display conf data upgrading banner. * * @since 2.1 * @access private + * @return void */ private function _in_upgrading() { include LSCWP_DIR . 'tpl/inc/in_upgrading.php'; } /** - * Output litespeed form info + * Output LiteSpeed form open tag and hidden fields. + * + * @since 3.0 * - * @since 3.0 - * @access public + * @param string|false $action Router action. + * @param string|false $type Router type. + * @param bool $has_upload Whether form has file uploads. + * @return void */ public function form_action( $action = false, $type = false, $has_upload = false ) { - if (!$action) { + if ( ! $action ) { $action = Router::ACTION_SAVE_SETTINGS; } - $has_upload = $has_upload ? 'enctype="multipart/form-data"' : ''; - - if (!defined('LITESPEED_CONF_LOADED')) { - echo '<div class="litespeed-relative"'; + if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) { + echo '<div class="litespeed-relative">'; } else { - echo '<form method="post" action="' . wp_unslash($_SERVER['REQUEST_URI']) . '" class="litespeed-relative" ' . $has_upload . '>'; + $current = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + if ( $has_upload ) { + echo '<form method="post" action="' . esc_url( $current ) . '" class="litespeed-relative" enctype="multipart/form-data">'; + } else { + echo '<form method="post" action="' . esc_url( $current ) . '" class="litespeed-relative">'; + } } - echo '<input type="hidden" name="' . Router::ACTION . '" value="' . $action . '" />'; - if ($type) { - echo '<input type="hidden" name="' . Router::TYPE . '" value="' . $type . '" />'; + echo '<input type="hidden" name="' . esc_attr( Router::ACTION ) . '" value="' . esc_attr( $action ) . '" />'; + if ( $type ) { + echo '<input type="hidden" name="' . esc_attr( Router::TYPE ) . '" value="' . esc_attr( $type ) . '" />'; } - wp_nonce_field($action, Router::NONCE); + wp_nonce_field( $action, Router::NONCE ); } /** - * Output litespeed form info END + * Output LiteSpeed form end (submit + closing tags). + * + * @since 3.0 * - * @since 3.0 - * @access public + * @return void */ - public function form_end( $disable_reset = false ) { + public function form_end() { echo "<div class='litespeed-top20'></div>"; - if (!defined('LITESPEED_CONF_LOADED')) { - submit_button(__('Save Changes', 'litespeed-cache'), 'secondary litespeed-duplicate-float', 'litespeed-submit', true, array( 'disabled' => 'disabled' )); + if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) { + submit_button( __( 'Save Changes', 'litespeed-cache' ), 'secondary litespeed-duplicate-float', 'litespeed-submit', true, array( 'disabled' => 'disabled' ) ); echo '</div>'; } else { - submit_button(__('Save Changes', 'litespeed-cache'), 'primary litespeed-duplicate-float', 'litespeed-submit', true, array( - 'id' => 'litespeed-submit-' . $this->_btn_i++, - )); + submit_button( + __( 'Save Changes', 'litespeed-cache' ), + 'primary litespeed-duplicate-float', + 'litespeed-submit', + true, + array( + 'id' => 'litespeed-submit-' . $this->_btn_i++, + ) + ); echo '</form>'; } } /** - * Register this setting to save + * Register a setting for saving. + * + * @since 3.0 * - * @since 3.0 - * @access public + * @param string $id Setting ID. + * @return void */ public function enroll( $id ) { - echo '<input type="hidden" name="' . Admin_Settings::ENROLL . '[]" value="' . $id . '" />'; + echo '<input type="hidden" name="' . esc_attr( Admin_Settings::ENROLL ) . '[]" value="' . esc_attr( $id ) . '" />'; } /** - * Build a textarea + * Build a textarea input. * * @since 1.1.0 - * @access public + * + * @param string $id Setting ID. + * @param int|false $cols Columns count. + * @param string|nil $val Pre-set value. + * @return void */ public function build_textarea( $id, $cols = false, $val = null ) { - if ($val === null) { - $val = $this->conf($id, true); + if ( null === $val ) { + $val = $this->conf( $id, true ); - if (is_array($val)) { - $val = implode("\n", $val); + if ( is_array( $val ) ) { + $val = implode( "\n", $val ); } } - if (!$cols) { + if ( ! $cols ) { $cols = 80; } + $rows = $this->get_textarea_rows( $val ); + + $this->enroll( $id ); + + echo "<textarea name='" . esc_attr( $id ) . "' rows='" . (int) $rows . "' cols='" . (int) $cols . "'>" . esc_textarea( $val ) . '</textarea>'; + + $this->_check_overwritten( $id ); + } + + /** + * Calculate textarea rows. + * + * @since 7.4 + * + * @param string $val Text area value. + * @return int Number of rows to use. + */ + public function get_textarea_rows( $val ) { $rows = 5; - $lines = substr_count($val, "\n") + 2; - if ($lines > $rows) { + $lines = substr_count( (string) $val, "\n" ) + 2; + if ( $lines > $rows ) { $rows = $lines; } - if ($rows > 40) { + if ( $rows > 40 ) { $rows = 40; } - $this->enroll($id); - - echo "<textarea name='$id' rows='$rows' cols='$cols'>" . esc_textarea($val) . '</textarea>'; - - $this->_check_overwritten($id); + return $rows; } /** - * Build a text input field + * Build a text input field. * * @since 1.1.0 - * @access public + * + * @param string $id Setting ID. + * @param string|null $cls CSS class. + * @param string|null $val Value. + * @param string $type Input type. + * @param bool $disabled Whether disabled. + * @return void */ public function build_input( $id, $cls = null, $val = null, $type = 'text', $disabled = false ) { - if ($val === null) { - $val = $this->conf($id, true); + if ( null === $val ) { + $val = $this->conf( $id, true ); - // Mask pswds - if ($this->_conf_pswd($id) && $val) { - $val = str_repeat('*', strlen($val)); + // Mask passwords. + if ( $this->_conf_pswd( $id ) && $val ) { + $val = str_repeat( '*', strlen( $val ) ); } } - $label_id = preg_replace('/\W/', '', $id); + $label_id = preg_replace( '/\W/', '', $id ); - if ($type == 'text') { + if ( 'text' === $type ) { $cls = "regular-text $cls"; } - if ($disabled) { - echo "<input type='$type' class='$cls' value='" . esc_textarea($val) . "' id='input_$label_id' disabled /> "; + if ( $disabled ) { + echo "<input type='" . esc_attr( $type ) . "' class='" . esc_attr( $cls ) . "' value='" . esc_attr( $val ) . "' id='input_" . esc_attr( $label_id ) . "' disabled /> "; } else { - $this->enroll($id); - echo "<input type='$type' class='$cls' name='$id' value='" . esc_textarea($val) . "' id='input_$label_id' /> "; + $this->enroll( $id ); + echo "<input type='" . esc_attr( $type ) . "' class='" . esc_attr( $cls ) . "' name='" . esc_attr( $id ) . "' value='" . esc_attr( $val ) . "' id='input_" . esc_attr( $label_id ) . "' /> "; } - $this->_check_overwritten($id); + $this->_check_overwritten( $id ); } /** - * Build a checkbox html snippet + * Build a checkbox HTML snippet. * * @since 1.1.0 - * @access public - * @param string $id - * @param string $title - * @param bool $checked + * + * @param string $id Setting ID. + * @param string $title Checkbox label (HTML allowed). + * @param bool|null $checked Whether checked. + * @param int|string $value Checkbox value. + * @return void */ public function build_checkbox( $id, $title, $checked = null, $value = 1 ) { - if ($checked === null && $this->conf($id, true)) { + if ( null === $checked && $this->conf( $id, true ) ) { $checked = true; } - $checked = $checked ? ' checked ' : ''; - $label_id = preg_replace('/\W/', '', $id); + $label_id = preg_replace( '/\W/', '', $id ); - if ($value !== 1) { + if ( 1 !== $value ) { $label_id .= '_' . $value; } - $this->enroll($id); + $this->enroll( $id ); echo "<div class='litespeed-tick'> - <input type='checkbox' name='$id' id='input_checkbox_$label_id' value='$value' $checked /> - <label for='input_checkbox_$label_id'>$title</label> - </div>"; + <input type='checkbox' name='" . esc_attr( $id ) . "' id='input_checkbox_" . esc_attr( $label_id ) . "' value='" . esc_attr( $value ) . "' " . checked( (bool) $checked, true, false ) . " /> + <label for='input_checkbox_" . esc_attr( $label_id ) . "'>" . wp_kses_post( $title ) . '</label> + </div>'; - $this->_check_overwritten($id); + $this->_check_overwritten( $id ); } /** - * Build a toggle checkbox html snippet + * Build a toggle checkbox snippet. * * @since 1.7 + * + * @param string $id Setting ID. + * @param bool|null $checked Whether enabled. + * @param string|null $title_on Label when on. + * @param string|null $title_off Label when off. + * @return void */ public function build_toggle( $id, $checked = null, $title_on = null, $title_off = null ) { - if ($checked === null && $this->conf($id, true)) { + if ( null === $checked && $this->conf( $id, true ) ) { $checked = true; } - if ($title_on === null) { - $title_on = __('ON', 'litespeed-cache'); - $title_off = __('OFF', 'litespeed-cache'); + if ( null === $title_on ) { + $title_on = __( 'ON', 'litespeed-cache' ); + $title_off = __( 'OFF', 'litespeed-cache' ); } $cls = $checked ? 'primary' : 'default litespeed-toggleoff'; - echo "<div class='litespeed-toggle litespeed-toggle-btn litespeed-toggle-btn-$cls' data-litespeed-toggle-on='primary' data-litespeed-toggle-off='default' data-litespeed_toggle_id='$id' > - <input name='$id' type='hidden' value='$checked' /> + echo "<div class='litespeed-toggle litespeed-toggle-btn litespeed-toggle-btn-" . esc_attr( $cls ) . "' data-litespeed-toggle-on='primary' data-litespeed-toggle-off='default' data-litespeed_toggle_id='" . esc_attr( $id ) . "' > + <input name='" . esc_attr( $id ) . "' type='hidden' value='" . esc_attr( $checked ) . "' /> <div class='litespeed-toggle-group'> - <label class='litespeed-toggle-btn litespeed-toggle-btn-primary litespeed-toggle-on'>$title_on</label> - <label class='litespeed-toggle-btn litespeed-toggle-btn-default litespeed-toggle-active litespeed-toggle-off'>$title_off</label> + <label class='litespeed-toggle-btn litespeed-toggle-btn-primary litespeed-toggle-on'>" . esc_html( $title_on ) . "</label> + <label class='litespeed-toggle-btn litespeed-toggle-btn-default litespeed-toggle-active litespeed-toggle-off'>" . esc_html( $title_off ) . "</label> <span class='litespeed-toggle-handle litespeed-toggle-btn litespeed-toggle-btn-default'></span> </div> </div>"; } /** - * Build a switch div html snippet + * Build a switch (radio) field. * * @since 1.1.0 - * @since 1.7 removed param $disable - * @access public + * @since 1.7 Removed $disable param. + * + * @param string $id Setting ID. + * @param array<int,mixed>|false $title_list Labels for options (OFF/ON). + * @return void */ public function build_switch( $id, $title_list = false ) { - $this->enroll($id); + $this->enroll( $id ); echo '<div class="litespeed-switch">'; - if (!$title_list) { - $title_list = array( __('OFF', 'litespeed-cache'), __('ON', 'litespeed-cache') ); + if ( ! $title_list ) { + $title_list = array( __( 'OFF', 'litespeed-cache' ), __( 'ON', 'litespeed-cache' ) ); } - foreach ($title_list as $k => $v) { - $this->_build_radio($id, $k, $v); + foreach ( $title_list as $k => $v ) { + $this->_build_radio( $id, $k, $v ); } echo '</div>'; - $this->_check_overwritten($id); + $this->_check_overwritten( $id ); } /** - * Build a radio input html codes and output + * Build a radio input and echo it. * * @since 1.1.0 * @access private + * + * @param string $id Setting ID. + * @param int|string $val Value for the radio. + * @param string $txt Label HTML. + * @return void */ private function _build_radio( $id, $val, $txt ) { - $id_attr = 'input_radio_' . preg_replace('/\W/', '', $id) . '_' . $val; + $id_attr = 'input_radio_' . preg_replace( '/\W/', '', $id ) . '_' . $val; - $default = isset(self::$_default_options[$id]) ? self::$_default_options[$id] : self::$_default_site_options[$id]; + $default = isset( self::$_default_options[ $id ] ) ? self::$_default_options[ $id ] : self::$_default_site_options[ $id ]; - if (!is_string($default)) { - $checked = (int) $this->conf($id, true) === (int) $val ? ' checked ' : ''; - } else { - $checked = $this->conf($id, true) === $val ? ' checked ' : ''; - } + $is_checked = ! is_string( $default ) + ? ( (int) $this->conf( $id, true ) === (int) $val ) + : ( $this->conf( $id, true ) === $val ); - echo "<input type='radio' autocomplete='off' name='$id' id='$id_attr' value='$val' $checked /> <label for='$id_attr'>$txt</label>"; + echo "<input type='radio' autocomplete='off' name='" . esc_attr( $id ) . "' id='" . esc_attr( $id_attr ) . "' value='" . esc_attr( $val ) . "' " . checked( $is_checked, true, false ) . " /> <label for='" . esc_attr( $id_attr ) . "'>" . wp_kses_post( $txt ) . '</label>'; } /** - * Show overwritten msg if there is a const defined + * Show overwritten info if value comes from const/primary/filter/server. + * + * @since 3.0 + * @since 7.4 Show value from filters. Added type parameter. * - * @since 3.0 + * @param string $id Setting ID. + * @return void */ protected function _check_overwritten( $id ) { - $const_val = $this->const_overwritten($id); - $primary_val = $this->primary_overwritten($id); - if ($const_val === null && $primary_val === null) { + $const_val = $this->const_overwritten( $id ); + $primary_val = $this->primary_overwritten( $id ); + $filter_val = $this->filter_overwritten( $id ); + $server_val = $this->server_overwritten( $id ); + + if ( null === $const_val && null === $primary_val && null === $filter_val && null === $server_val ) { return; } - $val = $const_val !== null ? $const_val : $primary_val; - - $default = isset(self::$_default_options[$id]) ? self::$_default_options[$id] : self::$_default_site_options[$id]; + // Get value to display. + $val = null !== $const_val ? $const_val : $primary_val; + // If we have filter_val will set as new val. + if ( null !== $filter_val ) { + $val = $filter_val; + } + // If we have server_val will set as new val. + if ( null !== $server_val ) { + $val = $server_val; + } - if (is_bool($default)) { - $val = $val ? __('ON', 'litespeed-cache') : __('OFF', 'litespeed-cache'); - } else { - if (is_array($default)) { - $val = implode("\n", $val); - } - $val = esc_textarea($val); + // Get type (used for display purpose). + $type = ( isset( self::$settings_filters[ $id ] ) && isset( self::$settings_filters[ $id ]['type'] ) ) ? self::$settings_filters[ $id ]['type'] : 'textarea'; + if ( ( null !== $const_val || null !== $primary_val ) && null === $filter_val ) { + $type = 'setting'; } - echo '<div class="litespeed-desc litespeed-warning">⚠️ '; + // Get default setting: if settings exist, use default setting, otherwise use filter/server value. + $default = ''; + if ( isset( self::$_default_options[ $id ] ) || isset( self::$_default_site_options[ $id ] ) ) { + $default = isset( self::$_default_options[ $id ] ) ? self::$_default_options[ $id ] : self::$_default_site_options[ $id ]; + } + if ( null !== $filter_val || null !== $server_val ) { + $default = null !== $filter_val ? $filter_val : $server_val; + } - if ($const_val !== null) { - printf(__('This setting is overwritten by the PHP constant %s', 'litespeed-cache'), '<code>' . Base::conf_const($id) . '</code>'); - } elseif (get_current_blog_id() != BLOG_ID_CURRENT_SITE && $this->conf(self::NETWORK_O_USE_PRIMARY)) { - echo __('This setting is overwritten by the primary site setting', 'litespeed-cache'); + // Set value to display, will be a string. + if ( is_bool( $default ) ) { + $val = $val ? __( 'ON', 'litespeed-cache' ) : __( 'OFF', 'litespeed-cache' ); } else { - echo __('This setting is overwritten by the Network setting', 'litespeed-cache'); - } + if ( is_array( $val ) ) { + $val = implode( "\n", $val ); + } + $val = esc_textarea( $val ); + } + + // Show warning for all types except textarea. + if ( 'textarea' !== $type ) { + echo '<div class="litespeed-desc litespeed-warning litespeed-overwrite">⚠️ '; + + if ( null !== $server_val ) { + // Show $_SERVER value. + printf( esc_html__( 'This value is overwritten by the %s variable.', 'litespeed-cache' ), '$_SERVER' ); + $val = '$_SERVER["' . $server_val[0] . '"] = ' . $server_val[1]; + } elseif ( null !== $filter_val ) { + // Show filter value. + echo esc_html__( 'This value is overwritten by the filter.', 'litespeed-cache' ); + } elseif ( null !== $const_val ) { + // Show CONSTANT value. + printf( esc_html__( 'This value is overwritten by the PHP constant %s.', 'litespeed-cache' ), '<code>' . esc_html( Base::conf_const( $id ) ) . '</code>' ); + } elseif ( is_multisite() ) { + // Show multisite overwrite. + if ( get_current_blog_id() !== BLOG_ID_CURRENT_SITE && $this->conf( self::NETWORK_O_USE_PRIMARY ) ) { + echo esc_html__( 'This value is overwritten by the primary site setting.', 'litespeed-cache' ); + } else { + echo esc_html__( 'This value is overwritten by the Network setting.', 'litespeed-cache' ); + } + } - echo ', ' . sprintf(__('currently set to %s', 'litespeed-cache'), "<code>$val</code>") . '</div>'; + echo ' ' . sprintf( esc_html__( 'Currently set to %s', 'litespeed-cache' ), '<code>' . esc_html( $val ) . '</code>' ) . '</div>'; + } elseif ( 'textarea' === $type && null !== $filter_val ) { + // Show warning for textarea. + // Textarea sizes. + $cols = 30; + $rows = $this->get_textarea_rows( $val ); + $rows_current_val = $this->get_textarea_rows( implode( "\n", $this->conf( $id, true ) ) ); + // If filter rows is bigger than textarea size, equalize them. + if ( $rows > $rows_current_val ) { + $rows = $rows_current_val; + } + ?> + <div class="litespeed-desc-wrapper"> + <div class="litespeed-desc"><?php echo esc_html__( 'Value from filter applied', 'litespeed-cache' ); ?>:</div> + <textarea readonly rows="<?php echo (int) $rows; ?>" cols="<?php echo (int) $cols; ?>"><?php echo esc_textarea( $val ); ?></textarea> + </div> + <?php + } } /** - * Display seconds text and readable layout + * Display seconds label and readable span. * * @since 3.0 - * @access public + * @return void */ public function readable_seconds() { - echo __('seconds', 'litespeed-cache'); + echo esc_html__( 'seconds', 'litespeed-cache' ); echo ' <span data-litespeed-readable=""></span>'; } /** - * Display default value + * Display default value for a setting. * - * @since 1.1.1 - * @access public + * @since 1.1.1 + * + * @param string $id Setting ID. + * @return void */ public function recommended( $id ) { - if (!$this->default_settings) { + if ( ! $this->default_settings ) { $this->default_settings = $this->load_default_vals(); } - $val = $this->default_settings[$id]; + $val = $this->default_settings[ $id ]; - if ($val) { - if (is_array($val)) { - $rows = 5; - $cols = 30; - // Flexible rows/cols - $lines = count($val) + 1; - $rows = min(max($lines, $rows), 40); - foreach ($val as $v) { - $cols = max(strlen($v), $cols); - } - $cols = min($cols, 150); + if ( ! $val ) { + return; + } - $val = implode("\n", $val); - $val = esc_textarea($val); - $val = '<div class="litespeed-desc">' . __('Default value', 'litespeed-cache') . ':</div>' . "<textarea readonly rows='$rows' cols='$cols'>$val</textarea>"; - } else { - $val = esc_textarea($val); - $val = "<code>$val</code>"; - $val = __('Default value', 'litespeed-cache') . ': ' . $val; - } - echo $val; + if ( ! is_array( $val ) ) { + printf( + '%s: <code>%s</code>', + esc_html__( 'Default value', 'litespeed-cache' ), + esc_html( $val ) + ); + return; } + + $rows = 5; + $cols = 30; + // Flexible rows/cols. + $lines = count( $val ) + 1; + $rows = min( max( $lines, $rows ), 40 ); + foreach ( $val as $v ) { + $cols = max( strlen( $v ), $cols ); + } + $cols = min( $cols, 150 ); + + $val = implode( "\n", $val ); + printf( + '<div class="litespeed-desc">%s:</div><textarea readonly rows="%d" cols="%d">%s</textarea>', + esc_html__( 'Default value', 'litespeed-cache' ), + (int) $rows, + (int) $cols, + esc_textarea( $val ) + ); } /** - * Validate rewrite rules regex syntax + * Validate rewrite rules regex syntax. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Setting ID. + * @return void */ protected function _validate_syntax( $id ) { - $val = $this->conf($id, true); + $val = $this->conf( $id, true ); - if (!$val) { + if ( ! $val ) { return; } - if (!is_array($val)) { + if ( ! is_array( $val ) ) { $val = array( $val ); } - foreach ($val as $v) { - if (!Utility::syntax_checker($v)) { - echo '<br /><font class="litespeed-warning"> ❌ ' . __('Invalid rewrite rule', 'litespeed-cache') . ': <code>' . $v . '</code></font>'; + foreach ( $val as $v ) { + if ( ! Utility::syntax_checker( $v ) ) { + echo '<br /><span class="litespeed-warning"> ❌ ' . esc_html__( 'Invalid rewrite rule', 'litespeed-cache' ) . ': <code>' . wp_kses_post( $v ) . '</code></span>'; } } } /** - * Validate if the htaccess path is valid + * Validate if the .htaccess path is valid. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Setting ID. + * @return void */ protected function _validate_htaccess_path( $id ) { - $val = $this->conf($id, true); - if (!$val) { + $val = $this->conf( $id, true ); + if ( ! $val ) { return; } - if (substr($val, -10) !== '/.htaccess') { - echo '<br /><font class="litespeed-warning"> ❌ ' . sprintf(__('Path must end with %s', 'litespeed-cache'), '<code>/.htaccess</code>') . '</font>'; + if ( '/.htaccess' !== substr( $val, -10 ) ) { + echo '<br /><span class="litespeed-warning"> ❌ ' . sprintf( esc_html__( 'Path must end with %s', 'litespeed-cache' ), '<code>/.htaccess</code>' ) . '</span>'; } } /** - * Check ttl instead of error when saving + * Check TTL ranges and show tips. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Setting ID. + * @param int|bool $min Minimum value (or false). + * @param int|bool $max Maximum value (or false). + * @param bool $allow_zero Whether zero is allowed. + * @return void */ protected function _validate_ttl( $id, $min = false, $max = false, $allow_zero = false ) { - $val = $this->conf($id, true); - - if ($allow_zero && !$val) { - // return; - } + $val = $this->conf( $id, true ); $tip = array(); - if ($min && $val < $min && (!$allow_zero || $val != 0)) { - $tip[] = __('Minimum value', 'litespeed-cache') . ': <code>' . $min . '</code>.'; + if ( $min && $val < $min && ( ! $allow_zero || 0 !== $val ) ) { + $tip[] = esc_html__( 'Minimum value', 'litespeed-cache' ) . ': <code>' . $min . '</code>.'; } - if ($max && $val > $max) { - $tip[] = __('Maximum value', 'litespeed-cache') . ': <code>' . $max . '</code>.'; + if ( $max && $val > $max ) { + $tip[] = esc_html__( 'Maximum value', 'litespeed-cache' ) . ': <code>' . $max . '</code>.'; } echo '<br />'; - if ($tip) { - echo '<font class="litespeed-warning"> ❌ ' . implode(' ', $tip) . '</font>'; + if ( $tip ) { + echo '<span class="litespeed-warning"> ❌ ' . wp_kses_post( implode( ' ', $tip ) ) . '</span>'; } $range = ''; - if ($allow_zero) { - $range .= __('Zero, or', 'litespeed-cache') . ' '; + if ( $allow_zero ) { + $range .= esc_html__( 'Zero, or', 'litespeed-cache' ) . ' '; } - if ($min && $max) { + if ( $min && $max ) { $range .= $min . ' - ' . $max; - } elseif ($min) { - $range .= __('Larger than', 'litespeed-cache') . ' ' . $min; - } elseif ($max) { - $range .= __('Smaller than', 'litespeed-cache') . ' ' . $max; + } elseif ( $min ) { + $range .= esc_html__( 'Larger than', 'litespeed-cache' ) . ' ' . $min; + } elseif ( $max ) { + $range .= esc_html__( 'Smaller than', 'litespeed-cache' ) . ' ' . $max; } - echo __('Value range', 'litespeed-cache') . ': <code>' . $range . '</code>'; + echo esc_html__( 'Value range', 'litespeed-cache' ) . ': <code>' . esc_html( $range ) . '</code>'; } /** - * Check if ip is valid + * Validate IPs in a list. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Setting ID. + * @return void */ protected function _validate_ip( $id ) { - $val = $this->conf($id, true); - if (!$val) { + $val = $this->conf( $id, true ); + if ( ! $val ) { return; } - if (!is_array($val)) { + if ( ! is_array( $val ) ) { $val = array( $val ); } $tip = array(); - foreach ($val as $v) { - if (!$v) { + foreach ( $val as $v ) { + if ( ! $v ) { continue; } - if (!\WP_Http::is_ip_address($v)) { - $tip[] = __('Invalid IP', 'litespeed-cache') . ': <code>' . esc_textarea($v) . '</code>.'; + if ( ! \WP_Http::is_ip_address( $v ) ) { + $tip[] = esc_html__( 'Invalid IP', 'litespeed-cache' ) . ': <code>' . esc_html( $v ) . '</code>.'; } } - if ($tip) { - echo '<br /><font class="litespeed-warning"> ❌ ' . implode(' ', $tip) . '</font>'; + if ( $tip ) { + echo '<br /><span class="litespeed-warning"> ❌ ' . wp_kses_post( implode( ' ', $tip ) ) . '</span>'; } } /** - * Display API environment variable support + * Display API environment variable support. * - * @since 1.8.3 + * @since 1.8.3 * @access protected + * + * @param string ...$args Server variable names. + * @return void */ - protected function _api_env_var() { - $args = func_get_args(); - $s = '<code>' . implode('</code>, <code>', $args) . '</code>'; - - echo '<font class="litespeed-success"> ' . - __('API', 'litespeed-cache') . - ': ' . - sprintf(__('Server variable(s) %s available to override this setting.', 'litespeed-cache'), $s); - - Doc::learn_more('https://docs.litespeedtech.com/lscache/lscwp/admin/#limiting-the-crawler'); + protected function _api_env_var( ...$args ) { + echo '<span class="litespeed-success"> ' . + esc_html__( 'API', 'litespeed-cache' ) . ': ' . + sprintf( + /* translators: %s: list of server variables in <code> tags */ + esc_html__( 'Server variable(s) %s available to override this setting.', 'litespeed-cache' ), + '<code>' . implode( '</code>, <code>', array_map( 'esc_html', $args ) ) . '</code>' + ) . + '</span>'; + + Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/admin/#limiting-the-crawler' ); } /** - * Display URI setting example + * Display URI setting example. * - * @since 2.6.1 + * @since 2.6.1 * @access protected + * @return void */ protected function _uri_usage_example() { - echo __('The URLs will be compared to the REQUEST_URI server variable.', 'litespeed-cache'); - echo ' ' . sprintf(__('For example, for %1$s, %2$s can be used here.', 'litespeed-cache'), '<code>/mypath/mypage?aa=bb</code>', '<code>mypage?aa=</code>'); + echo esc_html__( 'The URLs will be compared to the REQUEST_URI server variable.', 'litespeed-cache' ); + /* translators: 1: example URL, 2: pattern example */ + echo ' ' . sprintf( esc_html__( 'For example, for %1$s, %2$s can be used here.', 'litespeed-cache' ), '<code>/mypath/mypage?aa=bb</code>', '<code>mypage?aa=</code>' ); echo '<br /><i>'; - printf(__('To match the beginning, add %s to the beginning of the item.', 'litespeed-cache'), '<code>^</code>'); - echo ' ' . sprintf(__('To do an exact match, add %s to the end of the URL.', 'litespeed-cache'), '<code>$</code>'); - echo ' ' . __('One per line.', 'litespeed-cache'); + /* translators: %s: caret symbol */ + printf( esc_html__( 'To match the beginning, add %s to the beginning of the item.', 'litespeed-cache' ), '<code>^</code>' ); + /* translators: %s: dollar symbol */ + echo ' ' . sprintf( esc_html__( 'To do an exact match, add %s to the end of the URL.', 'litespeed-cache' ), '<code>$</code>' ); + echo ' ' . esc_html__( 'One per line.', 'litespeed-cache' ); echo '</i>'; } /** - * Return groups string + * Return pluralized strings. + * + * @since 2.0 * - * @since 2.0 - * @access public + * @param int $num Number. + * @param string $kind Kind of item (group|image). + * @return string */ public static function print_plural( $num, $kind = 'group' ) { - if ($num > 1) { - switch ($kind) { + if ( $num > 1 ) { + switch ( $kind ) { case 'group': - return sprintf(__('%s groups', 'litespeed-cache'), $num); + return sprintf( esc_html__( '%s groups', 'litespeed-cache' ), $num ); case 'image': - return sprintf(__('%s images', 'litespeed-cache'), $num); + return sprintf( esc_html__( '%s images', 'litespeed-cache' ), $num ); default: return $num; } } - switch ($kind) { + switch ( $kind ) { case 'group': - return sprintf(__('%s group', 'litespeed-cache'), $num); + return sprintf( esc_html__( '%s group', 'litespeed-cache' ), $num ); case 'image': - return sprintf(__('%s image', 'litespeed-cache'), $num); + return sprintf( esc_html__( '%s image', 'litespeed-cache' ), $num ); default: return $num; @@ -1245,22 +1578,26 @@ public static function print_plural( $num, $kind = 'group' ) { } /** - * Return guidance html + * Return guidance HTML. + * + * @since 2.0 * - * @since 2.0 - * @access public + * @param string $title Title HTML. + * @param array<int,string> $steps Steps list (HTML allowed). + * @param int|string $current_step Current step number or 'done'. + * @return string HTML for guidance widget. */ public static function guidance( $title, $steps, $current_step ) { - if ($current_step === 'done') { - $current_step = count($steps) + 1; + if ( 'done' === $current_step ) { + $current_step = count( $steps ) + 1; } - $percentage = ' (' . floor((($current_step - 1) * 100) / count($steps)) . '%)'; + $percentage = ' (' . floor( ( ( $current_step - 1 ) * 100 ) / count( $steps ) ) . '%)'; - $html = '<div class="litespeed-guide">' . '<h2>' . $title . $percentage . '</h2>' . '<ol>'; - foreach ($steps as $k => $v) { + $html = '<div class="litespeed-guide"><h2>' . $title . $percentage . '</h2><ol>'; + foreach ( $steps as $k => $v ) { $step = $k + 1; - if ($current_step > $step) { + if ( $current_step > $step ) { $html .= '<li class="litespeed-guide-done">'; } else { $html .= '<li>'; @@ -1274,37 +1611,40 @@ public static function guidance( $title, $steps, $current_step ) { } /** - * Check if has qc hide banner cookie or not + * Check whether has QC hide banner cookie. * * @since 7.1 + * + * @return bool */ public static function has_qc_hide_banner() { - return isset($_COOKIE[self::COOKIE_QC_HIDE_BANNER]) && time() - $_COOKIE[self::COOKIE_QC_HIDE_BANNER] < 86400 * 90; + return isset( $_COOKIE[ self::COOKIE_QC_HIDE_BANNER ] ) && ( time() - (int) $_COOKIE[ self::COOKIE_QC_HIDE_BANNER ] ) < 86400 * 90; } /** - * Set qc hide banner cookie + * Set QC hide banner cookie. * * @since 7.1 + * @return void */ public static function set_qc_hide_banner() { $expire = time() + 86400 * 365; - self::debug('Set qc hide banner cookie'); - setcookie(self::COOKIE_QC_HIDE_BANNER, time(), $expire, COOKIEPATH, COOKIE_DOMAIN); + self::debug( 'Set qc hide banner cookie' ); + setcookie( self::COOKIE_QC_HIDE_BANNER, time(), $expire, COOKIEPATH, COOKIE_DOMAIN ); } /** - * Handle all request actions from main cls + * Handle all request actions from main cls. * - * @since 7.1 - * @access public + * @since 7.1 + * @return void */ public function handler() { $type = Router::verify_type(); - switch ($type) { + switch ( $type ) { case self::TYPE_QC_HIDE_BANNER: - self::set_qc_hide_banner(); + self::set_qc_hide_banner(); break; default: diff --git a/src/admin-settings.cls.php b/src/admin-settings.cls.php index 904a6f63f..0d88bc0ca 100644 --- a/src/admin-settings.cls.php +++ b/src/admin-settings.cls.php @@ -1,153 +1,171 @@ <?php - /** * The admin settings handler of the plugin. * + * Handles saving and validating settings from the admin UI and network admin. + * * @since 1.1.0 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Admin_Settings + * + * Saves, sanitizes, and validates LiteSpeed Cache settings. + */ class Admin_Settings extends Base { + const LOG_TAG = '[Settings]'; const ENROLL = '_settings-enroll'; /** - * Save settings + * Save settings (single site). * - * Both $_POST and CLI can use this way + * Accepts data from $_POST or WP-CLI. + * Importers may call the Conf class directly. * - * Import will directly call conf.cls + * @since 3.0 * - * @since 3.0 - * @access public + * @param array<string,mixed> $raw_data Raw data from request/CLI. + * @return void */ public function save( $raw_data ) { - Debug2::debug('[Settings] saving'); + self::debug( 'saving' ); - if (empty($raw_data[self::ENROLL])) { - exit('No fields'); + if ( empty( $raw_data[ self::ENROLL ] ) ) { + wp_die( esc_html__( 'No fields', 'litespeed-cache' ) ); } - $raw_data = Admin::cleanup_text($raw_data); + $raw_data = Admin::cleanup_text( $raw_data ); - // Convert data to config format - $the_matrix = array(); - foreach (array_unique($raw_data[self::ENROLL]) as $id) { + // Convert data to config format. + $the_matrix = []; + foreach ( array_unique( $raw_data[ self::ENROLL ] ) as $id ) { $child = false; - // Drop array format - if (strpos($id, '[') !== false) { - if (strpos($id, self::O_CDN_MAPPING) === 0 || strpos($id, self::O_CRAWLER_COOKIES) === 0) { - // CDN child | Cookie Crawler settings - $child = substr($id, strpos($id, '[') + 1, strpos($id, ']') - strpos($id, '[') - 1); - $id = substr($id, 0, strpos($id, '[')); // Drop ending []; Compatible with xx[0] way from CLI + + // Drop array format. + if ( false !== strpos( $id, '[' ) ) { + if ( 0 === strpos( $id, self::O_CDN_MAPPING ) || 0 === strpos( $id, self::O_CRAWLER_COOKIES ) ) { + // CDN child | Cookie Crawler settings. + $child = substr( $id, strpos( $id, '[' ) + 1, strpos( $id, ']' ) - strpos( $id, '[' ) - 1 ); + // Drop ending []; Compatible with xx[0] way from CLI. + $id = substr( $id, 0, strpos( $id, '[' ) ); } else { - $id = substr($id, 0, strpos($id, '[')); // Drop ending [] + // Drop ending []. + $id = substr( $id, 0, strpos( $id, '[' ) ); } } - if (!array_key_exists($id, self::$_default_options)) { + if ( ! array_key_exists( $id, self::$_default_options ) ) { continue; } - // Validate $child - if ($id == self::O_CDN_MAPPING) { - if (!in_array($child, array( self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE ))) { + // Validate $child. + if ( self::O_CDN_MAPPING === $id ) { + if ( ! in_array( $child, [ self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE ], true ) ) { continue; } } - if ($id == self::O_CRAWLER_COOKIES) { - if (!in_array($child, array( self::CRWL_COOKIE_NAME, self::CRWL_COOKIE_VALS ))) { + if ( self::O_CRAWLER_COOKIES === $id ) { + if ( ! in_array( $child, [ self::CRWL_COOKIE_NAME, self::CRWL_COOKIE_VALS ], true ) ) { continue; } } - $data = false; - - if ($child) { - $data = !empty($raw_data[$id][$child]) ? $raw_data[$id][$child] : false; // []=xxx or [0]=xxx + // Pull value from request. + if ( $child ) { + // []=xxx or [0]=xxx + $data = ! empty( $raw_data[ $id ][ $child ] ) ? $raw_data[ $id ][ $child ] : $this->type_casting(false, $id); } else { - $data = !empty($raw_data[$id]) ? $raw_data[$id] : false; + $data = ! empty( $raw_data[ $id ] ) ? $raw_data[ $id ] : $this->type_casting(false, $id); } - /** - * Sanitize the value - */ - if ($id == self::O_CDN_MAPPING || $id == self::O_CRAWLER_COOKIES) { - // Use existing in queue data if existed (Only available when $child != false) - $data2 = array_key_exists($id, $the_matrix) ? $the_matrix[$id] : (defined('WP_CLI') && WP_CLI ? $this->conf($id) : array()); + // Sanitize/normalize complex fields. + if ( self::O_CDN_MAPPING === $id || self::O_CRAWLER_COOKIES === $id ) { + // Use existing queued data if available (only when $child != false). + $data2 = array_key_exists( $id, $the_matrix ) + ? $the_matrix[ $id ] + : ( defined( 'WP_CLI' ) && WP_CLI ? $this->conf( $id ) : [] ); } - switch ($id) { - case self::O_CRAWLER_ROLES: // Don't allow Editor/admin to be used in crawler role simulator - $data = Utility::sanitize_lines($data); - if ($data) { - foreach ($data as $k => $v) { - if (user_can($v, 'edit_posts')) { + + switch ( $id ) { + // Don't allow Editor/admin to be used in crawler role simulator. + case self::O_CRAWLER_ROLES: + $data = Utility::sanitize_lines( $data ); + if ( $data ) { + foreach ( $data as $k => $v ) { + if ( user_can( $v, 'edit_posts' ) ) { + /* translators: %s: user id in <code> tags */ $msg = sprintf( - __('The user with id %s has editor access, which is not allowed for the role simulator.', 'litespeed-cache'), - '<code>' . $v . '</code>' - ); - Admin_Display::error($msg); - unset($data[$k]); - } + esc_html__( 'The user with id %s has editor access, which is not allowed for the role simulator.', 'litespeed-cache' ), + '<code>' . esc_html( $v ) . '</code>' + ); + Admin_Display::error( $msg ); + unset( $data[ $k ] ); } + } } break; + case self::O_CDN_MAPPING: /** * CDN setting * * Raw data format: - * cdn-mapping[url][] = 'xxx' - * cdn-mapping[url][2] = 'xxx2' - * cdn-mapping[inc_js][] = 1 + * cdn-mapping[url][] = 'xxx' + * cdn-mapping[url][2] = 'xxx2' + * cdn-mapping[inc_js][] = 1 * * Final format: - * cdn-mapping[ 0 ][ url ] = 'xxx' - * cdn-mapping[ 2 ][ url ] = 'xxx2' + * cdn-mapping[0][url] = 'xxx' + * cdn-mapping[2][url] = 'xxx2' */ - if ($data && is_array($data)) { - foreach ($data as $k => $v) { - if ($child == self::CDN_MAPPING_FILETYPE) { - $v = Utility::sanitize_lines($v); + if ( $data ) { + foreach ( $data as $k => $v ) { + if ( self::CDN_MAPPING_FILETYPE === $child ) { + $v = Utility::sanitize_lines( $v ); // Remove from MAPPING FILETYPE extensions for IMAGES, CSS, JS - $remove_type = apply_filters('litespeed_cdn_save_filetypes_remove', array( - '.jpg', - '.jpeg', - '.png', - '.gif', - '.svg', - '.webp', - '.avif', - '.css', - '.js', - )); - $v = array_values(array_diff($v, $remove_type)); + $remove_type = apply_filters( + 'litespeed_cdn_save_filetypes_remove', + array( + '.jpg', + '.jpeg', + '.png', + '.gif', + '.svg', + '.webp', + '.avif', + '.css', + '.js', + ) + ); + $v = array_values( array_diff($v, $remove_type) ); } - if ($child == self::CDN_MAPPING_URL) { - # If not a valid URL, turn off CDN - if (strpos($v, 'https://') !== 0) { - self::debug('❌ CDN mapping set to OFF due to invalid URL'); - $the_matrix[self::O_CDN] = false; + + if ( self::CDN_MAPPING_URL === $child ) { + // If not a valid URL, turn off CDN. + if ( 0 !== strpos( $v, 'https://' ) ) { + self::debug( '❌ CDN mapping set to OFF due to invalid URL' ); + $the_matrix[ self::O_CDN ] = false; } - $v = trailingslashit($v); + $v = trailingslashit( $v ); } - if (in_array($child, array(self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS))) { - // Because these can't be auto detected in `config->update()`, need to format here - $v = $v === 'false' ? 0 : (bool) $v; + + if ( in_array( $child, [ self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS ], true ) ) { + // Because these can't be auto detected in `config->update()`, need to format here. + $v = 'false' === $v ? 0 : (bool) $v; } - if (empty($data2[$k])) { - $data2[$k] = array(); + if ( empty( $data2[ $k ] ) ) { + $data2[ $k ] = []; } - $data2[$k][$child] = $v; + $data2[ $k ][ $child ] = $v; } } @@ -155,233 +173,247 @@ public function save( $raw_data ) { break; case self::O_CRAWLER_COOKIES: - /** - * Cookie Crawler setting - * Raw Format: - * crawler-cookies[name][] = xxx - * crawler-cookies[name][2] = xxx2 - * crawler-cookies[vals][] = xxx - * - * todo: need to allow null for values - * - * Final format: - * crawler-cookie[ 0 ][ name ] = 'xxx' - * crawler-cookie[ 0 ][ vals ] = 'xxx' - * crawler-cookie[ 2 ][ name ] = 'xxx2' - * - * empty line for `vals` use literal `_null` - */ - if ($data) { - foreach ($data as $k => $v) { - if ($child == self::CRWL_COOKIE_VALS) { - $v = Utility::sanitize_lines($v); - } - - if (empty($data2[$k])) { - $data2[$k] = array(); - } - - $data2[$k][$child] = $v; + /** + * Cookie Crawler setting + * Raw Format: + * crawler-cookies[name][] = xxx + * crawler-cookies[name][2] = xxx2 + * crawler-cookies[vals][] = xxx + * + * Final format: + * crawler-cookie[0][name] = 'xxx' + * crawler-cookie[0][vals] = 'xxx' + * crawler-cookie[2][name] = 'xxx2' + * + * Empty line for `vals` uses literal `_null`. + */ + if ( $data ) { + foreach ( $data as $k => $v ) { + if ( self::CRWL_COOKIE_VALS === $child ) { + $v = Utility::sanitize_lines( $v ); } + + if ( empty( $data2[ $k ] ) ) { + $data2[ $k ] = []; + } + + $data2[ $k ][ $child ] = $v; + } } - $data = $data2; + $data = $data2; break; - case self::O_CACHE_EXC_CAT: // Cache exclude cat - $data2 = array(); - $data = Utility::sanitize_lines($data); - foreach ($data as $v) { - $cat_id = get_cat_ID($v); - if (!$cat_id) { - continue; - } - + // Cache exclude category. + case self::O_CACHE_EXC_CAT: + $data2 = []; + $data = Utility::sanitize_lines( $data ); + foreach ( $data as $v ) { + $cat_id = get_cat_ID( $v ); + if ( ! $cat_id ) { + continue; + } $data2[] = $cat_id; } - $data = $data2; + $data = $data2; break; - case self::O_CACHE_EXC_TAG: // Cache exclude tag - $data2 = array(); - $data = Utility::sanitize_lines($data); - foreach ($data as $v) { - $term = get_term_by('name', $v, 'post_tag'); - if (!$term) { - // todo: can show the error in admin error msg - continue; - } - + // Cache exclude tag. + case self::O_CACHE_EXC_TAG: + $data2 = []; + $data = Utility::sanitize_lines( $data ); + foreach ( $data as $v ) { + $term = get_term_by( 'name', $v, 'post_tag' ); + if ( ! $term ) { + // Could surface an admin error here if desired. + continue; + } $data2[] = $term->term_id; } - $data = $data2; + $data = $data2; + break; + + case self::O_IMG_OPTM_SIZES_SKIPPED: // Skip image sizes + $image_sizes = Utility::prepare_image_sizes_array(); + $saved_sizes = isset( $raw_data[$id] ) ? $raw_data[$id] : []; + $data = array_diff( $image_sizes, $saved_sizes ); break; default: break; } - $the_matrix[$id] = $data; + $the_matrix[ $id ] = $data; } - - // Special handler for CDN/Crawler 2d list to drop empty rows - foreach ($the_matrix as $id => $data) { + + // Special handler for CDN/Crawler 2d list to drop empty rows. + foreach ( $the_matrix as $id => $data ) { /** - * cdn-mapping[ 0 ][ url ] = 'xxx' - * cdn-mapping[ 2 ][ url ] = 'xxx2' - * - * crawler-cookie[ 0 ][ name ] = 'xxx' - * crawler-cookie[ 0 ][ vals ] = 'xxx' - * crawler-cookie[ 2 ][ name ] = 'xxx2' + * Format: + * cdn-mapping[0][url] = 'xxx' + * cdn-mapping[2][url] = 'xxx2' + * crawler-cookie[0][name] = 'xxx' + * crawler-cookie[0][vals] = 'xxx' + * crawler-cookie[2][name] = 'xxx2' */ - if ($id == self::O_CDN_MAPPING || $id == self::O_CRAWLER_COOKIES) { - // Drop this line if all children elements are empty - foreach ($data as $k => $v) { - foreach ($v as $v2) { - if ($v2) { + if ( self::O_CDN_MAPPING === $id || self::O_CRAWLER_COOKIES === $id ) { + // Drop row if all children are empty. + foreach ( $data as $k => $v ) { + foreach ( $v as $v2 ) { + if ( $v2 ) { continue 2; } } - // If hit here, means all empty - unset($the_matrix[$id][$k]); + // All empty. + unset( $the_matrix[ $id ][ $k ] ); } } - // Don't allow repeated cookie name - if ($id == self::O_CRAWLER_COOKIES) { - $existed = array(); - foreach ($the_matrix[$id] as $k => $v) { - if (!$v[self::CRWL_COOKIE_NAME] || in_array($v[self::CRWL_COOKIE_NAME], $existed)) { - // Filter repeated or empty name - unset($the_matrix[$id][$k]); + // Don't allow repeated cookie names. + if ( self::O_CRAWLER_COOKIES === $id ) { + $existed = []; + foreach ( $the_matrix[ $id ] as $k => $v ) { + if ( empty( $v[ self::CRWL_COOKIE_NAME ] ) || in_array( $v[ self::CRWL_COOKIE_NAME ], $existed, true ) ) { + // Filter repeated or empty name. + unset( $the_matrix[ $id ][ $k ] ); continue; } - $existed[] = $v[self::CRWL_COOKIE_NAME]; + $existed[] = $v[ self::CRWL_COOKIE_NAME ]; } } - // CDN mapping allow URL values repeated - // if ( $id == self::O_CDN_MAPPING ) {} - - // tmp fix the 3rd part woo update hook issue when enabling vary cookie - if ($id == 'wc_cart_vary') { - if ($data) { - add_filter('litespeed_vary_cookies', function ( $list ) { - $list[] = 'woocommerce_cart_hash'; - return array_unique($list); - }); + // tmp fix the 3rd part woo update hook issue when enabling vary cookie. + if ( 'wc_cart_vary' === $id ) { + if ( $data ) { + add_filter( + 'litespeed_vary_cookies', + function ( $arr ) { + $arr[] = 'woocommerce_cart_hash'; + return array_unique( $arr ); + } + ); } else { - add_filter('litespeed_vary_cookies', function ( $list ) { - if (in_array('woocommerce_cart_hash', $list)) { - unset($list[array_search('woocommerce_cart_hash', $list)]); + add_filter( + 'litespeed_vary_cookies', + function ( $arr ) { + $key = array_search( 'woocommerce_cart_hash', $arr, true ); + if ( false !== $key ) { + unset( $arr[ $key ] ); + } + return array_unique( $arr ); } - return array_unique($list); - }); + ); } } } - // id validation will be inside - $this->cls('Conf')->update_confs($the_matrix); + // id validation will be inside. + $this->cls( 'Conf' )->update_confs( $the_matrix ); - $msg = __('Options saved.', 'litespeed-cache'); - Admin_Display::success($msg); + $msg = __( 'Options saved.', 'litespeed-cache' ); + Admin_Display::success( $msg ); } /** * Parses any changes made by the network admin on the network settings. * * @since 3.0 - * @access public + * + * @param array<string,mixed> $raw_data Raw data from request/CLI. + * @return void */ public function network_save( $raw_data ) { - Debug2::debug('[Settings] network saving'); + self::debug( 'network saving' ); - if (empty($raw_data[self::ENROLL])) { - exit('No fields'); + if ( empty( $raw_data[ self::ENROLL ] ) ) { + wp_die( esc_html__( 'No fields', 'litespeed-cache' ) ); } - $raw_data = Admin::cleanup_text($raw_data); + $raw_data = Admin::cleanup_text( $raw_data ); - foreach (array_unique($raw_data[self::ENROLL]) as $id) { - // Append current field to setting save - if (!array_key_exists($id, self::$_default_site_options)) { + foreach ( array_unique( $raw_data[ self::ENROLL ] ) as $id ) { + // Append current field to setting save. + if ( ! array_key_exists( $id, self::$_default_site_options ) ) { continue; } - $data = !empty($raw_data[$id]) ? $raw_data[$id] : false; + $data = ! empty( $raw_data[ $id ] ) ? $raw_data[ $id ] : false; - // id validation will be inside - $this->cls('Conf')->network_update($id, $data); + // id validation will be inside. + $this->cls( 'Conf' )->network_update( $id, $data ); } - // Update related files + // Update related files. Activation::cls()->update_files(); - $msg = __('Options saved.', 'litespeed-cache'); - Admin_Display::success($msg); + $msg = __( 'Options saved.', 'litespeed-cache' ); + Admin_Display::success( $msg ); } /** - * Hooked to the wp_redirect filter. - * This will only hook if there was a problem when saving the widget. + * Hooked to the wp_redirect filter when saving widgets fails validation. * * @since 1.1.3 - * @access public - * @param string $location The location string. - * @return string the updated location string. + * + * @param string $location The redirect location. + * @return string Updated location string. */ public static function widget_save_err( $location ) { - return str_replace('?message=0', '?error=0', $location); + return str_replace( '?message=0', '?error=0', $location ); } /** - * Hooked to the widget_update_callback filter. - * Validate the LiteSpeed Cache settings on edit widget save. + * Validate the LiteSpeed Cache settings on widget save. * * @since 1.1.3 - * @access public - * @param array $instance The new settings. - * @param array $new_instance - * @param array $old_instance The original settings. - * @param WP_Widget $widget The widget - * @return mixed Updated settings on success, false on error. + * + * @param array $instance The new settings. + * @param array $new_instance The raw submitted settings. + * @param array $old_instance The original settings. + * @param \WP_Widget $widget The widget instance. + * @return array|false Updated settings on success, false on error. */ public static function validate_widget_save( $instance, $new_instance, $old_instance, $widget ) { - if (empty($new_instance)) { + if ( empty( $new_instance ) ) { return $instance; } - if (!isset($new_instance[ESI::WIDGET_O_ESIENABLE]) || !isset($new_instance[ESI::WIDGET_O_TTL])) { + if ( ! isset( $new_instance[ ESI::WIDGET_O_ESIENABLE ], $new_instance[ ESI::WIDGET_O_TTL ] ) ) { return $instance; } - $esi = intval($new_instance[ESI::WIDGET_O_ESIENABLE]) % 3; - $ttl = (int) $new_instance[ESI::WIDGET_O_TTL]; + $esi = (int) $new_instance[ ESI::WIDGET_O_ESIENABLE ] % 3; + $ttl = (int) $new_instance[ ESI::WIDGET_O_TTL ]; - if ($ttl != 0 && $ttl < 30) { - add_filter('wp_redirect', __CLASS__ . '::widget_save_err'); - return false; // invalid ttl. + if ( 0 !== $ttl && $ttl < 30 ) { + add_filter( 'wp_redirect', __CLASS__ . '::widget_save_err' ); + return false; // Invalid ttl. } - if (empty($instance[Conf::OPTION_NAME])) { - // todo: to be removed - $instance[Conf::OPTION_NAME] = array(); + if ( empty( $instance[ Conf::OPTION_NAME ] ) ) { + // @todo to be removed. + $instance[ Conf::OPTION_NAME ] = []; } - $instance[Conf::OPTION_NAME][ESI::WIDGET_O_ESIENABLE] = $esi; - $instance[Conf::OPTION_NAME][ESI::WIDGET_O_TTL] = $ttl; - - $current = !empty($old_instance[Conf::OPTION_NAME]) ? $old_instance[Conf::OPTION_NAME] : false; - if (!strpos($_SERVER['HTTP_REFERER'], '/wp-admin/customize.php')) { - if (!$current || $esi != $current[ESI::WIDGET_O_ESIENABLE]) { - Purge::purge_all('Widget ESI_enable changed'); - } elseif ($ttl != 0 && $ttl != $current[ESI::WIDGET_O_TTL]) { - Purge::add(Tag::TYPE_WIDGET . $widget->id); + $instance[ Conf::OPTION_NAME ][ ESI::WIDGET_O_ESIENABLE ] = $esi; + $instance[ Conf::OPTION_NAME ][ ESI::WIDGET_O_TTL ] = $ttl; + + $current = ! empty( $old_instance[ Conf::OPTION_NAME ] ) ? $old_instance[ Conf::OPTION_NAME ] : false; + + // Avoid unsanitized superglobal usage. + $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : ''; + + // Only purge when not in the Customizer. + if ( false === strpos( $referrer, '/wp-admin/customize.php' ) ) { + if ( ! $current || $esi !== (int) $current[ ESI::WIDGET_O_ESIENABLE ] ) { + Purge::purge_all( 'Widget ESI_enable changed' ); + } elseif ( 0 !== $ttl && $ttl !== (int) $current[ ESI::WIDGET_O_TTL ] ) { + Purge::add( Tag::TYPE_WIDGET . $widget->id ); } - Purge::purge_all('Widget saved'); + Purge::purge_all( 'Widget saved' ); } + return $instance; } } diff --git a/src/admin.cls.php b/src/admin.cls.php index 23f5307e0..a09c66048 100644 --- a/src/admin.cls.php +++ b/src/admin.cls.php @@ -4,13 +4,17 @@ * * @since 1.0.0 * @package LiteSpeed_Cache - * @subpackage LiteSpeed_Cache/admin - * @author LiteSpeed Technologies <info@litespeedtech.com> */ + namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Admin + * + * Wires admin-side hooks, actions, and safe redirects. + */ class Admin extends Root { const LOG_TAG = '👮'; @@ -19,97 +23,100 @@ class Admin extends Root { /** * Initialize the class and set its properties. - * Run in hook `after_setup_theme` when is_admin() + * Runs in hook `after_setup_theme` when is_admin(). * - * @since 1.0.0 + * @since 1.0.0 */ public function __construct() { - // Define LSCWP_MU_PLUGIN if is mu-plugins - if (defined('WPMU_PLUGIN_DIR') && dirname(LSCWP_DIR) == WPMU_PLUGIN_DIR) { - define('LSCWP_MU_PLUGIN', true); + // Define LSCWP_MU_PLUGIN if in mu-plugins. + if ( defined( 'WPMU_PLUGIN_DIR' ) && dirname( LSCWP_DIR ) === WPMU_PLUGIN_DIR && ! defined( 'LSCWP_MU_PLUGIN' ) ) { + define( 'LSCWP_MU_PLUGIN', true ); } - self::debug('No cache due to Admin page'); - defined('DONOTCACHEPAGE') || define('DONOTCACHEPAGE', true); + self::debug( 'No cache due to Admin page' ); - // Additional litespeed assets on admin display - // Also register menu - $this->cls('Admin_Display'); + if ( ! defined( 'DONOTCACHEPAGE' ) ) { + define( 'DONOTCACHEPAGE', true ); + } + + // Additional LiteSpeed assets on admin display (also registers menus). + $this->cls( 'Admin_Display' ); + + // Initialize admin actions. + add_action( 'admin_init', array( $this, 'admin_init' ) ); - // initialize admin actions - add_action('admin_init', array( $this, 'admin_init' )); - // add link to plugin list page - add_filter('plugin_action_links_' . LSCWP_BASENAME, array( $this->cls('Admin_Display'), 'add_plugin_links' )); + // Add link to plugin list page. + add_filter( + 'plugin_action_links_' . LSCWP_BASENAME, + array( $this->cls( 'Admin_Display' ), 'add_plugin_links' ) + ); } /** * Callback that initializes the admin options for LiteSpeed Cache. * * @since 1.0.0 - * @access public + * @return void */ public function admin_init() { - // Hook attachment upload - if ($this->conf(Base::O_IMG_OPTM_AUTO)) { - add_filter('wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 9999, 2); + // Hook attachment upload auto optimization. + if ( $this->conf( Base::O_IMG_OPTM_AUTO ) ) { + add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 9999, 2 ); } $this->_proceed_admin_action(); - // Terminate if user doesn't have the access to settings - if (is_network_admin()) { - $capability = 'manage_network_options'; - } else { - $capability = 'manage_options'; - } - if (!current_user_can($capability)) { + // Terminate if user doesn't have access to settings. + $capability = is_network_admin() ? 'manage_network_options' : 'manage_options'; + if ( ! current_user_can( $capability ) ) { return; } - // Save setting from admin settings page - // NOTE: cli will call `validate_plugin_settings` manually. Cron activation doesn't need to validate - - // Add privacy policy - // @since 2.2.6 - if (function_exists('wp_add_privacy_policy_content')) { - wp_add_privacy_policy_content(Core::NAME, Doc::privacy_policy()); + // Add privacy policy (since 2.2.6). + if ( function_exists( 'wp_add_privacy_policy_content' ) ) { + wp_add_privacy_policy_content( Core::NAME, Doc::privacy_policy() ); } - $this->cls('Media')->after_admin_init(); + $this->cls( 'Media' )->after_admin_init(); - do_action('litspeed_after_admin_init'); + do_action( 'litespeed_after_admin_init' ); - if ($this->cls('Router')->esi_enabled()) { - add_action('in_widget_form', array( $this->cls('Admin_Display'), 'show_widget_edit' ), 100, 3); - add_filter('widget_update_callback', __NAMESPACE__ . '\Admin_Settings::validate_widget_save', 10, 4); + if ( $this->cls( 'Router' )->esi_enabled() ) { + add_action( 'in_widget_form', array( $this->cls( 'Admin_Display' ), 'show_widget_edit' ), 100, 3 ); + add_filter( 'widget_update_callback', __NAMESPACE__ . '\Admin_Settings::validate_widget_save', 10, 4 ); } } /** - * Handle attachment update + * Handle attachment metadata update. * - * @since 4.0 + * @since 4.0 + * + * @param array $data Attachment meta. + * @param int $post_id Attachment ID. + * @return array Filtered meta. */ public function wp_update_attachment_metadata( $data, $post_id ) { - $this->cls('Img_Optm')->wp_update_attachment_metadata($data, $post_id); + $this->cls( 'Img_Optm' )->wp_update_attachment_metadata( $data, $post_id ); return $data; } /** - * Run litespeed admin actions + * Run LiteSpeed admin actions routed via Router. * * @since 1.1.0 + * @return void */ private function _proceed_admin_action() { - // handle actions - switch (Router::get_action()) { + $action = Router::get_action(); + + switch ( $action ) { case Router::ACTION_SAVE_SETTINGS: - $this->cls('Admin_Settings')->save($_POST); + $this->cls( 'Admin_Settings' )->save( wp_unslash( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing break; - // Save network settings case Router::ACTION_SAVE_SETTINGS_NETWORK: - $this->cls('Admin_Settings')->network_save($_POST); + $this->cls( 'Admin_Settings' )->network_save( wp_unslash( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing break; default: @@ -118,66 +125,65 @@ private function _proceed_admin_action() { } /** - * Clean up the input string of any extra slashes/spaces. + * Clean up the input (array or scalar) of any extra slashes/spaces. * * @since 1.0.4 - * @access public - * @param string $input The input string to clean. - * @return string The cleaned up input. + * + * @param mixed $input The input value to clean. + * @return mixed Cleaned value. */ public static function cleanup_text( $input ) { - if (is_array($input)) { - return array_map(__CLASS__ . '::cleanup_text', $input); + if ( is_array( $input ) ) { + return array_map( __CLASS__ . '::cleanup_text', $input ); } return stripslashes(trim($input)); } /** - * After a LSCWP_CTRL action, need to redirect back to the same page - * without the nonce and action in the query string. + * After a LSCWP_CTRL action, redirect back to same page + * without nonce and action in the query string. * - * If the redirect url cannot be determined, redirects to the homepage. + * If the redirect URL cannot be determined, redirects to the homepage. * * @since 1.0.12 - * @access public - * @global string $pagenow + * + * @param string|false $url Optional destination URL. + * @return void */ public static function redirect( $url = false ) { global $pagenow; - if (!empty($_GET['_litespeed_ori'])) { - wp_safe_redirect(wp_get_referer() ?: get_home_url()); - exit(); + // If originated, go back to referrer or home. + if ( ! empty( $_GET['_litespeed_ori'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $ref = wp_get_referer(); + wp_safe_redirect( $ref ? $ref : get_home_url() ); + exit; } - $qs = ''; - if (!$url) { - if (!empty($_GET)) { - if (isset($_GET[Router::ACTION])) { - unset($_GET[Router::ACTION]); - } - if (isset($_GET[Router::NONCE])) { - unset($_GET[Router::NONCE]); - } - if (isset($_GET[Router::TYPE])) { - unset($_GET[Router::TYPE]); - } - if (isset($_GET['litespeed_i'])) { - unset($_GET['litespeed_i']); - } - if (!empty($_GET)) { - $qs = '?' . http_build_query($_GET); + if ( ! $url ) { + $clean = []; + + // Sanitize current query args while removing our internals. + if ( ! empty( $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + foreach ( $_GET as $k => $v ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( in_array( $k, array( Router::ACTION, Router::NONCE, Router::TYPE, 'litespeed_i' ), true ) ) { + continue; + } + // Normalize to string for URL building. + $clean[ $k ] = is_array( $v ) ? array_map( 'sanitize_text_field', wp_unslash( $v ) ) : sanitize_text_field( wp_unslash( $v ) ); } } - if (is_network_admin()) { - $url = network_admin_url($pagenow . $qs); - } else { - $url = admin_url($pagenow . $qs); + + $qs = ''; + if ( ! empty( $clean ) ) { + $qs = '?' . http_build_query( $clean ); } + + $url = is_network_admin() ? network_admin_url( $pagenow . $qs ) : admin_url( $pagenow . $qs ); } - wp_redirect($url); - exit(); + wp_safe_redirect( $url ); + exit; } } diff --git a/src/api.cls.php b/src/api.cls.php index b367e7b7d..b944266df 100644 --- a/src/api.cls.php +++ b/src/api.cls.php @@ -3,15 +3,20 @@ * The plugin API class. * * @since 1.1.3 - * @since 1.4 Moved into /inc * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ + namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class API + * + * Provides API hooks and methods for LiteSpeed Cache integration. + * + * @since 1.1.3 + */ class API extends Base { const VERSION = Core::VER; @@ -39,6 +44,8 @@ class API extends Base { /** * Instance * + * Initializes the API class. + * * @since 3.0 */ public function __construct() { @@ -55,148 +62,154 @@ public function init() { /** * Init */ - // Action `litespeed_init` // @previous API::hook_init( $hook ) + // Action `litespeed_init` /** * Conf */ - add_filter('litespeed_conf', array( $this, 'conf' )); // @previous API::config($id) - // Action `litespeed_conf_append` // @previous API::conf_append( $name, $default ) - add_action('litespeed_conf_multi_switch', __NAMESPACE__ . '\Base::set_multi_switch', 10, 2); - // Action ``litespeed_conf_force` // @previous API::force_option( $k, $v ) - add_action( 'litespeed_save_conf', array( $this, 'save_conf')); + add_filter( 'litespeed_conf', array( $this, 'conf' ) ); + // Action `litespeed_conf_append` + add_action( 'litespeed_conf_multi_switch', __NAMESPACE__ . '\Base::set_multi_switch', 10, 2 ); + // Action `litespeed_conf_force` + add_action( 'litespeed_save_conf', array( $this, 'save_conf' ) ); /** * Cache Control Hooks */ - // Action `litespeed_control_finalize` // @previous API::hook_control($tags) && action `litespeed_api_control` - add_action('litespeed_control_set_private', __NAMESPACE__ . '\Control::set_private'); // @previous API::set_cache_private() - add_action('litespeed_control_set_nocache', __NAMESPACE__ . '\Control::set_nocache'); // @previous API::set_nocache( $reason = false ) - add_action('litespeed_control_set_cacheable', array( $this, 'set_cacheable' )); // Might needed if not call hook `wp` // @previous API::set_cacheable( $reason ) - add_action('litespeed_control_force_cacheable', __NAMESPACE__ . '\Control::force_cacheable'); // Set cache status to force cacheable ( Will ignore most kinds of non-cacheable conditions ) // @previous API::set_force_cacheable( $reason ) - add_action('litespeed_control_force_public', __NAMESPACE__ . '\Control::set_public_forced'); // Set cache to force public cache if cacheable ( Will ignore most kinds of non-cacheable conditions ) // @previous API::set_force_public( $reason ) - add_filter('litespeed_control_cacheable', __NAMESPACE__ . '\Control::is_cacheable', 3); // Note: Read-Only. Directly append to this filter won't work. Call actions above to set cacheable or not // @previous API::not_cacheable() - add_action('litespeed_control_set_ttl', __NAMESPACE__ . '\Control::set_custom_ttl', 10, 2); // @previous API::set_ttl( $val ) - add_filter('litespeed_control_ttl', array( $this, 'get_ttl' ), 3); // @previous API::get_ttl() + // Action `litespeed_control_finalize` + add_action( 'litespeed_control_set_private', __NAMESPACE__ . '\Control::set_private' ); + add_action( 'litespeed_control_set_nocache', __NAMESPACE__ . '\Control::set_nocache' ); + add_action( 'litespeed_control_set_cacheable', array( $this, 'set_cacheable' ) ); + add_action( 'litespeed_control_force_cacheable', __NAMESPACE__ . '\Control::force_cacheable' ); + add_action( 'litespeed_control_force_public', __NAMESPACE__ . '\Control::set_public_forced' ); + add_filter( 'litespeed_control_cacheable', __NAMESPACE__ . '\Control::is_cacheable', 3 ); + add_action( 'litespeed_control_set_ttl', __NAMESPACE__ . '\Control::set_custom_ttl', 10, 2 ); + add_filter( 'litespeed_control_ttl', array( $this, 'get_ttl' ), 3 ); /** * Tag Hooks */ - // Action `litespeed_tag_finalize` // @previous API::hook_tag( $hook ) - add_action('litespeed_tag', __NAMESPACE__ . '\Tag::add'); // Shorter alias of `litespeed_tag_add` - add_action('litespeed_tag_post', __NAMESPACE__ . '\Tag::add_post'); // Shorter alias of `litespeed_tag_add_post` - add_action('litespeed_tag_widget', __NAMESPACE__ . '\Tag::add_widget'); // Shorter alias of `litespeed_tag_add_widget` - add_action('litespeed_tag_private', __NAMESPACE__ . '\Tag::add_private'); // Shorter alias of `litespeed_tag_add_private` - add_action('litespeed_tag_private_esi', __NAMESPACE__ . '\Tag::add_private_esi'); // Shorter alias of `litespeed_tag_add_private_esi` - - add_action('litespeed_tag_add', __NAMESPACE__ . '\Tag::add'); // @previous API::tag_add( $tag ) - add_action('litespeed_tag_add_post', __NAMESPACE__ . '\Tag::add_post'); - add_action('litespeed_tag_add_widget', __NAMESPACE__ . '\Tag::add_widget'); - add_action('litespeed_tag_add_private', __NAMESPACE__ . '\Tag::add_private'); // @previous API::tag_add_private( $tags ) - add_action('litespeed_tag_add_private_esi', __NAMESPACE__ . '\Tag::add_private_esi'); + // Action `litespeed_tag_finalize` + add_action( 'litespeed_tag', __NAMESPACE__ . '\Tag::add' ); + add_action( 'litespeed_tag_post', __NAMESPACE__ . '\Tag::add_post' ); + add_action( 'litespeed_tag_widget', __NAMESPACE__ . '\Tag::add_widget' ); + add_action( 'litespeed_tag_private', __NAMESPACE__ . '\Tag::add_private' ); + add_action( 'litespeed_tag_private_esi', __NAMESPACE__ . '\Tag::add_private_esi' ); + + add_action( 'litespeed_tag_add', __NAMESPACE__ . '\Tag::add' ); + add_action( 'litespeed_tag_add_post', __NAMESPACE__ . '\Tag::add_post' ); + add_action( 'litespeed_tag_add_widget', __NAMESPACE__ . '\Tag::add_widget' ); + add_action( 'litespeed_tag_add_private', __NAMESPACE__ . '\Tag::add_private' ); + add_action( 'litespeed_tag_add_private_esi', __NAMESPACE__ . '\Tag::add_private_esi' ); /** * Purge Hooks */ - // Action `litespeed_purge_finalize` // @previous API::hook_purge($tags) - add_action('litespeed_purge', __NAMESPACE__ . '\Purge::add'); // @previous API::purge($tags) - add_action('litespeed_purge_all', __NAMESPACE__ . '\Purge::purge_all'); - add_action('litespeed_purge_post', array( $this, 'purge_post' )); // @previous API::purge_post( $pid ) - add_action('litespeed_purge_posttype', __NAMESPACE__ . '\Purge::purge_posttype'); - add_action('litespeed_purge_url', array( $this, 'purge_url' )); - add_action('litespeed_purge_widget', __NAMESPACE__ . '\Purge::purge_widget'); - add_action('litespeed_purge_esi', __NAMESPACE__ . '\Purge::purge_esi'); - add_action('litespeed_purge_private', __NAMESPACE__ . '\Purge::add_private'); // @previous API::purge_private( $tags ) - add_action('litespeed_purge_private_esi', __NAMESPACE__ . '\Purge::add_private_esi'); - add_action('litespeed_purge_private_all', __NAMESPACE__ . '\Purge::add_private_all'); // @previous API::purge_private_all() - // Action `litespeed_api_purge_post` // Triggered when purge a post // @previous API::hook_purge_post($hook) - // Action `litespeed_purged_all` // Triggered after purged all. - add_action('litespeed_purge_all_object', __NAMESPACE__ . '\Purge::purge_all_object'); - add_action('litespeed_purge_ucss', __NAMESPACE__ . '\Purge::purge_ucss'); + // Action `litespeed_purge_finalize` + add_action( 'litespeed_purge', __NAMESPACE__ . '\Purge::add' ); + add_action( 'litespeed_purge_all', __NAMESPACE__ . '\Purge::purge_all' ); + add_action( 'litespeed_purge_post', array( $this, 'purge_post' ) ); + add_action( 'litespeed_purge_posttype', __NAMESPACE__ . '\Purge::purge_posttype' ); + add_action( 'litespeed_purge_url', array( $this, 'purge_url' ) ); + add_action( 'litespeed_purge_widget', __NAMESPACE__ . '\Purge::purge_widget' ); + add_action( 'litespeed_purge_esi', __NAMESPACE__ . '\Purge::purge_esi' ); + add_action( 'litespeed_purge_private', __NAMESPACE__ . '\Purge::add_private' ); + add_action( 'litespeed_purge_private_esi', __NAMESPACE__ . '\Purge::add_private_esi' ); + add_action( 'litespeed_purge_private_all', __NAMESPACE__ . '\Purge::add_private_all' ); + // Action `litespeed_api_purge_post` + // Action `litespeed_purged_all` + add_action( 'litespeed_purge_all_object', __NAMESPACE__ . '\Purge::purge_all_object' ); + add_action( 'litespeed_purge_ucss', __NAMESPACE__ . '\Purge::purge_ucss' ); /** * ESI */ - // Action `litespeed_nonce` // @previous API::nonce_action( $action ) & API::nonce( $action = -1, $defence_for_html_filter = true ) // NOTE: only available after `init` hook - add_filter('litespeed_esi_status', array( $this, 'esi_enabled' )); // Get ESI enable status // @previous API::esi_enabled() - add_filter('litespeed_esi_url', array( $this, 'sub_esi_block' ), 10, 8); // Generate ESI block url // @previous API::esi_url( $block_id, $wrapper, $params = array(), $control = 'private,no-vary', $silence = false, $preserved = false, $svar = false, $inline_val = false ) - // Filter `litespeed_widget_default_options` // Hook widget default settings value. Currently used in Woo 3rd // @previous API::hook_widget_default_options( $hook ) - // Filter `litespeed_esi_params` // @previous API::hook_esi_param( $hook ) - // Action `litespeed_tpl_normal` // @previous API::hook_tpl_not_esi($hook) && Action `litespeed_is_not_esi_template` - // Action `litespeed_esi_load-$block` // @usage add_action( 'litespeed_esi_load-' . $block, $hook ) // @previous API::hook_tpl_esi($block, $hook) - add_action('litespeed_esi_combine', __NAMESPACE__ . '\ESI::combine'); + // Action `litespeed_nonce` + add_filter( 'litespeed_esi_status', array( $this, 'esi_enabled' ) ); + add_filter( 'litespeed_esi_url', array( $this, 'sub_esi_block' ), 10, 8 ); // Generate ESI block url + // Filter `litespeed_widget_default_options` // Hook widget default settings value. Currently used in Woo 3rd + // Filter `litespeed_esi_params` + // Action `litespeed_tpl_normal` + // Action `litespeed_esi_load-$block` // @usage add_action( 'litespeed_esi_load-' . $block, $hook ) + add_action( 'litespeed_esi_combine', __NAMESPACE__ . '\ESI::combine' ); /** * Vary * * To modify default vary, There are two ways: Action `litespeed_vary_append` or Filter `litespeed_vary` */ - add_action('litespeed_vary_ajax_force', __NAMESPACE__ . '\Vary::can_ajax_vary'); // API::force_vary() -> Action `litespeed_vary_ajax_force` // Force finalize vary even if its in an AJAX call + add_action( 'litespeed_vary_ajax_force', __NAMESPACE__ . '\Vary::can_ajax_vary' ); // Force finalize vary even if its in an AJAX call // Filter `litespeed_vary_curr_cookies` to generate current in use vary, which will be used for response vary header. // Filter `litespeed_vary_cookies` to register the final vary cookies, which will be written to rewrite rule. (litespeed_vary_curr_cookies are always equal to or less than litespeed_vary_cookies) - // Filter `litespeed_vary` // Previous API::hook_vary_finalize( $hook ) - add_action('litespeed_vary_no', __NAMESPACE__ . '\Control::set_no_vary'); // API::set_cache_no_vary() -> Action `litespeed_vary_no` // Set cache status to no vary - - // add_filter( 'litespeed_is_mobile', __NAMESPACE__ . '\Control::is_mobile' ); // API::set_mobile() -> Filter `litespeed_is_mobile` + // Filter `litespeed_vary` + add_action( 'litespeed_vary_no', __NAMESPACE__ . '\Control::set_no_vary' ); /** * Cloud */ - add_filter('litespeed_is_from_cloud', array( $this, 'is_from_cloud' )); // Check if current request is from QC (usually its to check REST access) // @see https://wordpress.org/support/topic/image-optimization-not-working-3/ + add_filter( 'litespeed_is_from_cloud', array( $this, 'is_from_cloud' ) ); // Check if current request is from QC (usually its to check REST access) // @see https://wordpress.org/support/topic/image-optimization-not-working-3/ /** * Media */ - add_action('litespeed_media_reset', __NAMESPACE__ . '\Media::delete_attachment'); // Reset one media row + add_action( 'litespeed_media_reset', __NAMESPACE__ . '\Media::delete_attachment' ); /** * GUI */ - // API::clean_wrapper_begin( $counter = false ) -> Filter `litespeed_clean_wrapper_begin` // Start a to-be-removed html wrapper - add_filter('litespeed_clean_wrapper_begin', __NAMESPACE__ . '\GUI::clean_wrapper_begin'); - // API::clean_wrapper_end( $counter = false ) -> Filter `litespeed_clean_wrapper_end` // End a to-be-removed html wrapper - add_filter('litespeed_clean_wrapper_end', __NAMESPACE__ . '\GUI::clean_wrapper_end'); + add_filter( 'litespeed_clean_wrapper_begin', __NAMESPACE__ . '\GUI::clean_wrapper_begin' ); + add_filter( 'litespeed_clean_wrapper_end', __NAMESPACE__ . '\GUI::clean_wrapper_end' ); /** - * Mist + * Misc */ - add_action('litespeed_debug', __NAMESPACE__ . '\Debug2::debug', 10, 2); // API::debug()-> Action `litespeed_debug` - add_action('litespeed_debug2', __NAMESPACE__ . '\Debug2::debug2', 10, 2); // API::debug2()-> Action `litespeed_debug2` - add_action('litespeed_disable_all', array( $this, '_disable_all' )); // API::disable_all( $reason ) -> Action `litespeed_disable_all` + add_action( 'litespeed_debug', __NAMESPACE__ . '\Debug2::debug', 10, 2 ); + add_action( 'litespeed_debug2', __NAMESPACE__ . '\Debug2::debug2', 10, 2 ); + add_action( 'litespeed_disable_all', array( $this, 'disable_all' ) ); - add_action('litspeed_after_admin_init', array( $this, '_after_admin_init' )); + add_action( 'litespeed_after_admin_init', array( $this, 'after_admin_init' ) ); } /** * API for admin related * + * Registers hooks for admin settings and UI elements. + * * @since 3.0 * @access public */ - public function _after_admin_init() { + public function after_admin_init() { /** * GUI */ - add_action('litespeed_setting_enroll', array( $this->cls('Admin_Display'), 'enroll' ), 10, 4); // API::enroll( $id ) // Register a field in setting form to save - add_action('litespeed_build_switch', array( $this->cls('Admin_Display'), 'build_switch' )); // API::build_switch( $id ) // Build a switch div html snippet - // API::hook_setting_content( $hook, $priority = 10, $args = 1 ) -> Action `litespeed_settings_content` - // API::hook_setting_tab( $hook, $priority = 10, $args = 1 ) -> Action `litespeed_settings_tab` + add_action( 'litespeed_setting_enroll', array( $this->cls( 'Admin_Display' ), 'enroll' ), 10, 4 ); + add_action( 'litespeed_build_switch', array( $this->cls( 'Admin_Display' ), 'build_switch' ) ); + // Action `litespeed_settings_content` + // Action `litespeed_settings_tab` } /** - * Disable All (Note: Not for direct call, always use Hooks) + * Disable All + * + * Disables all LiteSpeed Cache features with a given reason. * * @since 2.9.7.2 * @access public + * @param string $reason The reason for disabling all features. */ - public function _disable_all( $reason ) { - do_action('litespeed_debug', '[API] Disabled_all due to ' . $reason); + public function disable_all( $reason ) { + do_action( 'litespeed_debug', '[API] Disabled_all due to ' . $reason ); - !defined('LITESPEED_DISABLE_ALL') && define('LITESPEED_DISABLE_ALL', true); + ! defined( 'LITESPEED_DISABLE_ALL' ) && define( 'LITESPEED_DISABLE_ALL', true ); } /** + * Append commenter vary + * + * Adds commenter vary to the cache vary cookies. + * * @since 3.0 + * @access public */ public static function vary_append_commenter() { Vary::cls()->append_commenter(); @@ -205,32 +218,98 @@ public static function vary_append_commenter() { /** * Check if is from Cloud * + * Checks if the current request originates from QUIC.cloud. + * * @since 4.2 + * @access public + * @return bool True if from QUIC.cloud, false otherwise. */ public function is_from_cloud() { - return $this->cls('Cloud')->is_from_cloud(); + return $this->cls( 'Cloud' )->is_from_cloud(); } + /** + * Purge post + * + * Purges the cache for a specific post. + * + * @since 3.0 + * @access public + * @param int $pid Post ID to purge. + */ public function purge_post( $pid ) { - $this->cls('Purge')->purge_post($pid); + $this->cls( 'Purge' )->purge_post( $pid ); } + /** + * Purge URL + * + * Purges the cache for a specific URL. + * + * @since 3.0 + * @access public + * @param string $url URL to purge. + */ public function purge_url( $url ) { - $this->cls('Purge')->purge_url($url); + $this->cls( 'Purge' )->purge_url( $url ); } + /** + * Set cacheable + * + * Marks the current request as cacheable. + * + * @since 3.0 + * @access public + * @param string|bool $reason Optional reason for setting cacheable. + */ public function set_cacheable( $reason = false ) { - $this->cls('Control')->set_cacheable($reason); + $this->cls( 'Control' )->set_cacheable( $reason ); } + /** + * Check ESI enabled + * + * Returns whether ESI is enabled. + * + * @since 3.0 + * @access public + * @return bool True if ESI is enabled, false otherwise. + */ public function esi_enabled() { - return $this->cls('Router')->esi_enabled(); + return $this->cls( 'Router' )->esi_enabled(); } + /** + * Get TTL + * + * Retrieves the cache TTL (time to live). + * + * @since 3.0 + * @access public + * @return int Cache TTL value. + */ public function get_ttl() { - return $this->cls('Control')->get_ttl(); + return $this->cls( 'Control' )->get_ttl(); } + /** + * Generate ESI block URL + * + * Generates a URL for an ESI block. + * + * @since 3.0 + * @access public + * @param string $block_id ESI block ID. + * @param string $wrapper Wrapper identifier. + * @param array $params Parameters for the ESI block. + * @param string $control Cache control settings. + * @param bool $silence Silence output flag. + * @param bool $preserved Preserved flag. + * @param bool $svar Server variable flag. + * @param array $inline_param Inline parameters. + * @return string ESI block URL. + */ public function sub_esi_block( $block_id, $wrapper, @@ -241,15 +320,19 @@ public function sub_esi_block( $svar = false, $inline_param = array() ) { - return $this->cls('ESI')->sub_esi_block($block_id, $wrapper, $params, $control, $silence, $preserved, $svar, $inline_param); + return $this->cls( 'ESI' )->sub_esi_block( $block_id, $wrapper, $params, $control, $silence, $preserved, $svar, $inline_param ); } /** * Set and sync conf * + * Updates and synchronizes configuration settings. + * * @since 7.2 + * @access public + * @param bool|array $the_matrix Configuration data to update. */ public function save_conf( $the_matrix = false ) { - $this->cls('Conf')->update_confs( $the_matrix ); + $this->cls( 'Conf' )->update_confs( $the_matrix ); } } diff --git a/src/avatar.cls.php b/src/avatar.cls.php index a047b4e9b..0fe682520 100644 --- a/src/avatar.cls.php +++ b/src/avatar.cls.php @@ -1,288 +1,366 @@ <?php - /** - * The avatar cache class + * The avatar cache class. + * + * Caches remote (e.g., Gravatar) avatars locally and rewrites URLs + * to serve cached copies with a TTL. Supports on-demand generation + * during page render and batch generation via cron. * - * @since 3.0 - * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> + * @since 3.0 + * @package LiteSpeed */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Avatar + */ class Avatar extends Base { const TYPE_GENERATE = 'generate'; + /** + * Avatar cache TTL (seconds). + * + * @var int + */ private $_conf_cache_ttl; + + /** + * Avatar DB table name. + * + * @var string + */ private $_tb; + /** + * In-request map from original URL => rewritten URL to avoid duplicates. + * + * @var array<string,string> + */ private $_avatar_realtime_gen_dict = array(); + + /** + * Summary/status data for last requests. + * + * @var array<string,mixed> + */ protected $_summary; /** - * Init + * Init. * - * @since 1.4 + * @since 1.4 */ public function __construct() { - if (!$this->conf(self::O_DISCUSS_AVATAR_CACHE)) { + if ( ! $this->conf( self::O_DISCUSS_AVATAR_CACHE ) ) { return; } - Debug2::debug2('[Avatar] init'); + self::debug2( '[Avatar] init' ); - $this->_tb = $this->cls('Data')->tb('avatar'); + $this->_tb = $this->cls( 'Data' )->tb( 'avatar' ); - $this->_conf_cache_ttl = $this->conf(self::O_DISCUSS_AVATAR_CACHE_TTL); + $this->_conf_cache_ttl = $this->conf( self::O_DISCUSS_AVATAR_CACHE_TTL ); - add_filter('get_avatar_url', array( $this, 'crawl_avatar' )); + add_filter( 'get_avatar_url', array( $this, 'crawl_avatar' ) ); $this->_summary = self::get_summary(); } /** - * Check if need db table or not + * Check whether DB table is needed. * * @since 3.0 * @access public + * @return bool */ public function need_db() { - if ($this->conf(self::O_DISCUSS_AVATAR_CACHE)) { - return true; - } - - return false; + return (bool) $this->conf( self::O_DISCUSS_AVATAR_CACHE ); } + /** - * Get gravatar URL from DB and regenerate + * Serve static avatar by md5 (used by local static route). * - * @since 3.0 + * @since 3.0 * @access public + * @param string $md5 MD5 hash of original avatar URL. + * @return void */ public function serve_static( $md5 ) { global $wpdb; - Debug2::debug('[Avatar] is avatar request'); + self::debug( '[Avatar] is avatar request' ); - if (strlen($md5) !== 32) { - Debug2::debug('[Avatar] wrong md5 ' . $md5); + if ( strlen( $md5 ) !== 32 ) { + self::debug( '[Avatar] wrong md5 ' . $md5 ); return; } - $q = "SELECT url FROM `$this->_tb` WHERE md5=%s"; - $url = $wpdb->get_var($wpdb->prepare($q, $md5)); + $url = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->prepare( + 'SELECT url FROM `' . $this->_tb . '` WHERE md5 = %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $md5 + ) + ); - if (!$url) { - Debug2::debug('[Avatar] no matched url for md5 ' . $md5); + if ( ! $url ) { + self::debug( '[Avatar] no matched url for md5 ' . $md5 ); return; } - $url = $this->_generate($url); + $url = $this->_generate( $url ); - wp_redirect($url); - exit(); + wp_safe_redirect( $url ); + exit; } /** - * Localize gravatar + * Localize/replace avatar URL with cached one (filter callback). * - * @since 3.0 + * @since 3.0 * @access public + * @param string $url Original avatar URL. + * @return string Rewritten/cached avatar URL (or original). */ public function crawl_avatar( $url ) { - if (!$url) { + if ( ! $url ) { return $url; } - // Check if its already in dict or not - if (!empty($this->_avatar_realtime_gen_dict[$url])) { - Debug2::debug2('[Avatar] already in dict [url] ' . $url); - - return $this->_avatar_realtime_gen_dict[$url]; + // Check if already generated in this request. + if ( ! empty( $this->_avatar_realtime_gen_dict[ $url ] ) ) { + self::debug2( '[Avatar] already in dict [url] ' . $url ); + return $this->_avatar_realtime_gen_dict[ $url ]; } - $realpath = $this->_realpath($url); - if (file_exists($realpath) && time() - filemtime($realpath) <= $this->_conf_cache_ttl) { - Debug2::debug2('[Avatar] cache file exists [url] ' . $url); - return $this->_rewrite($url, filemtime($realpath)); + $realpath = $this->_realpath( $url ); + $mtime = file_exists( $realpath ) ? filemtime( $realpath ) : false; + + if ( $mtime && time() - $mtime <= $this->_conf_cache_ttl ) { + self::debug2( '[Avatar] cache file exists [url] ' . $url ); + return $this->_rewrite( $url, $mtime ); } - if (!strpos($url, 'gravatar.com')) { + // Only handle gravatar or known remote avatar providers; keep generic check for "gravatar.com". + if ( strpos( $url, 'gravatar.com' ) === false ) { return $url; } - // Send request - if (!empty($this->_summary['curr_request']) && time() - $this->_summary['curr_request'] < 300) { - Debug2::debug2('[Avatar] Bypass generating due to interval limit [url] ' . $url); + // Throttle generation. + if ( ! empty( $this->_summary['curr_request'] ) && time() - $this->_summary['curr_request'] < 300 ) { + self::debug2( '[Avatar] Bypass generating due to interval limit [url] ' . $url ); return $url; } - // Generate immediately - $this->_avatar_realtime_gen_dict[$url] = $this->_generate($url); + // Generate immediately and track for this request. + $this->_avatar_realtime_gen_dict[ $url ] = $this->_generate( $url ); - return $this->_avatar_realtime_gen_dict[$url]; + return $this->_avatar_realtime_gen_dict[ $url ]; } /** - * Read last time generated info + * Count queued avatars (expired ones) for cron. * - * @since 3.0 + * @since 3.0 * @access public + * @return int|false */ public function queue_count() { global $wpdb; - // If var not exists, mean table not exists // todo: not true - if (!$this->_tb) { + // If var not exists, means table not exists // todo: not true. + if ( ! $this->_tb ) { return false; } - $q = "SELECT COUNT(*) FROM `$this->_tb` WHERE dateline<" . (time() - $this->_conf_cache_ttl); - return $wpdb->get_var($q); + $cnt = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->prepare( + 'SELECT COUNT(*) FROM `' . $this->_tb . '` WHERE dateline < %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + time() - $this->_conf_cache_ttl + ) + ); + + return (int) $cnt; } /** - * Get the final URL of local avatar - * - * Check from db also + * Build final local URL for cached avatar. * - * @since 3.0 + * @since 3.0 + * @param string $url Original URL. + * @param int|null $time Optional filemtime for cache busting. + * @return string Local URL. */ private function _rewrite( $url, $time = null ) { - return LITESPEED_STATIC_URL . '/avatar/' . $this->_filepath($url) . ($time ? '?ver=' . $time : ''); + $qs = $time ? '?ver=' . $time : ''; + return LITESPEED_STATIC_URL . '/avatar/' . $this->_filepath( $url ) . $qs; } /** - * Generate realpath of the cache file + * Generate filesystem realpath for cache file. * - * @since 3.0 + * @since 3.0 * @access private + * @param string $url Original URL. + * @return string Absolute filesystem path. */ private function _realpath( $url ) { - return LITESPEED_STATIC_DIR . '/avatar/' . $this->_filepath($url); + return LITESPEED_STATIC_DIR . '/avatar/' . $this->_filepath( $url ); } /** - * Get filepath + * Get relative filepath for cached avatar. * - * @since 4.0 + * @since 4.0 + * @param string $url Original URL. + * @return string Relative path under avatar/ (may include blog id). */ private function _filepath( $url ) { - $filename = md5($url) . '.jpg'; - if (is_multisite()) { + $filename = md5( $url ) . '.jpg'; + if ( is_multisite() ) { $filename = get_current_blog_id() . '/' . $filename; } return $filename; } /** - * Cron generation + * Cron generation for expired avatars. * - * @since 3.0 + * @since 3.0 * @access public + * @param bool $force Bypass throttle. + * @return void */ public static function cron( $force = false ) { global $wpdb; $_instance = self::cls(); - if (!$_instance->queue_count()) { - Debug2::debug('[Avatar] no queue'); + if ( ! $_instance->queue_count() ) { + self::debug( '[Avatar] no queue' ); return; } - // For cron, need to check request interval too - if (!$force) { - if (!empty($_instance->_summary['curr_request']) && time() - $_instance->_summary['curr_request'] < 300) { - Debug2::debug('[Avatar] curr_request too close'); + // For cron, need to check request interval too. + if ( ! $force ) { + if ( ! empty( $_instance->_summary['curr_request'] ) && time() - $_instance->_summary['curr_request'] < 300 ) { + self::debug( '[Avatar] curr_request too close' ); return; } } - $q = "SELECT url FROM `$_instance->_tb` WHERE dateline < %d ORDER BY id DESC LIMIT %d"; - $q = $wpdb->prepare($q, array( time() - $_instance->_conf_cache_ttl, apply_filters('litespeed_avatar_limit', 30) )); - - $list = $wpdb->get_results($q); - Debug2::debug('[Avatar] cron job [count] ' . count($list)); - - foreach ($list as $v) { - Debug2::debug('[Avatar] cron job [url] ' . $v->url); - - $_instance->_generate($v->url); + $list = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->prepare( + 'SELECT url FROM `' . $_instance->_tb . '` WHERE dateline < %d ORDER BY id DESC LIMIT %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + time() - $_instance->_conf_cache_ttl, + (int) apply_filters( 'litespeed_avatar_limit', 30 ) + ) + ); + self::debug( '[Avatar] cron job [count] ' . ( $list ? count( $list ) : 0 ) ); + + if ( $list ) { + foreach ( $list as $v ) { + self::debug( '[Avatar] cron job [url] ' . $v->url ); + $_instance->_generate( $v->url ); + } } } /** - * Remote generator + * Download and store the avatar locally, then update DB row. * - * @since 3.0 + * @since 3.0 * @access private + * @param string $url Original avatar URL. + * @return string Rewritten local URL (fallback to original on failure). */ private function _generate( $url ) { global $wpdb; - // Record the data - - $file = $this->_realpath($url); + $file = $this->_realpath( $url ); - // Update request status - self::save_summary(array( 'curr_request' => time() )); + // Mark request start + self::save_summary( + array( + 'curr_request' => time(), + ) + ); - // Generate - $this->_maybe_mk_cache_folder('avatar'); + // Ensure cache directory exists + $this->_maybe_mk_cache_folder( 'avatar' ); - $response = wp_safe_remote_get($url, array( - 'timeout' => 180, - 'stream' => true, - 'filename' => $file, - )); + $response = wp_safe_remote_get( + $url, + array( + 'timeout' => 180, + 'stream' => true, + 'filename' => $file, + ) + ); - Debug2::debug('[Avatar] _generate [url] ' . $url); + self::debug( '[Avatar] _generate [url] ' . $url ); // Parse response data - if (is_wp_error($response)) { + if ( is_wp_error( $response ) ) { $error_message = $response->get_error_message(); - file_exists($file) && unlink($file); - Debug2::debug('[Avatar] failed to get: ' . $error_message); + if ( file_exists( $file ) ) { + wp_delete_file( $file ); + } + self::debug( '[Avatar] failed to get: ' . $error_message ); return $url; } // Save summary data - self::save_summary(array( - 'last_spent' => time() - $this->_summary['curr_request'], - 'last_request' => $this->_summary['curr_request'], - 'curr_request' => 0, - )); - - // Update DB - $md5 = md5($url); - $q = "UPDATE `$this->_tb` SET dateline=%d WHERE md5=%s"; - $existed = $wpdb->query($wpdb->prepare($q, array( time(), $md5 ))); - if (!$existed) { - $q = "INSERT INTO `$this->_tb` SET url=%s, md5=%s, dateline=%d"; - $wpdb->query($wpdb->prepare($q, array( $url, $md5, time() ))); + self::save_summary( + array( + 'last_spent' => time() - $this->_summary['curr_request'], + 'last_request' => $this->_summary['curr_request'], + 'curr_request' => 0, + ) + ); + + // Update/insert DB record + $md5 = md5( $url ); + + $existed = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->prepare( + 'UPDATE `' . $this->_tb . '` SET dateline = %d WHERE md5 = %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + time(), + $md5 + ) + ); + + if ( ! $existed ) { + $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->prepare( + 'INSERT INTO `' . $this->_tb . '` (url, md5, dateline) VALUES (%s, %s, %d)', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $url, + $md5, + time() + ) + ); } - Debug2::debug('[Avatar] saved avatar ' . $file); + self::debug( '[Avatar] saved avatar ' . $file ); - return $this->_rewrite($url); + return $this->_rewrite( $url ); } /** - * Handle all request actions from main cls + * Handle all request actions from main cls. * - * @since 3.0 + * @since 3.0 * @access public + * @return void */ public function handler() { $type = Router::verify_type(); - switch ($type) { + switch ( $type ) { case self::TYPE_GENERATE: - self::cron(true); + self::cron( true ); break; default: diff --git a/src/base.cls.php b/src/base.cls.php index bc3d1ef9f..4fce69f0e 100644 --- a/src/base.cls.php +++ b/src/base.cls.php @@ -1,15 +1,22 @@ <?php - /** - * The base consts + * The base constants and defaults for LiteSpeed Cache. + * + * Defines all option keys, default values, and helper methods shared across the plugin. * - * @since 3.7 + * @since 3.7 + * @package LiteSpeed */ namespace LiteSpeed; defined('WPINC') || exit(); +/** + * Class Base + * + * Core definitions and helpers shared across LiteSpeed Cache. + */ class Base extends Root { // This is redundant since v3.0 @@ -24,7 +31,7 @@ class Base extends Root { const _VER = '_version'; // Not set-able const HASH = 'hash'; // Not set-able const O_AUTO_UPGRADE = 'auto_upgrade'; - const O_API_KEY = 'api_key'; // Deprecated since v6.4. TODO: Will drop after v6.5 + const O_API_KEY = 'api_key'; // Deprecated since v6.4. TODO: Will drop after v8 (still need to keep for v7 upgrade conf usage as v6.5.4 has a major user base) const O_SERVER_IP = 'server_ip'; const O_GUEST = 'guest'; const O_GUEST_OPTM = 'guest_optm'; @@ -40,7 +47,6 @@ class Base extends Root { const O_CACHE_COMMENTER = 'cache-commenter'; const O_CACHE_REST = 'cache-rest'; const O_CACHE_PAGE_LOGIN = 'cache-page_login'; - const O_CACHE_FAVICON = 'cache-favicon'; // Deprecated since v6.2. TODO: Will drop after v6.5 const O_CACHE_RES = 'cache-resources'; // Deprecated since v7.2. TODO: Drop after v7.5 const O_CACHE_MOBILE = 'cache-mobile'; const O_CACHE_MOBILE_RULES = 'cache-mobile_rules'; @@ -218,6 +224,7 @@ class Base extends Root { const O_MEDIA_VPI = 'media-vpi'; const O_MEDIA_VPI_CRON = 'media-vpi_cron'; const O_IMG_OPTM_JPG_QUALITY = 'img_optm-jpg_quality'; + const O_MEDIA_AUTO_RESCALE_ORI = 'media-auto_rescale_ori'; // -------------------------------------------------- ## // -------------- Image Optm ----------------- ## @@ -228,6 +235,7 @@ class Base extends Root { const O_IMG_OPTM_RM_BKUP = 'img_optm-rm_bkup'; const O_IMG_OPTM_WEBP = 'img_optm-webp'; const O_IMG_OPTM_LOSSLESS = 'img_optm-lossless'; + const O_IMG_OPTM_SIZES_SKIPPED = 'img_optm-sizes_skipped'; const O_IMG_OPTM_EXIF = 'img_optm-exif'; const O_IMG_OPTM_WEBP_ATTR = 'img_optm-webp_attr'; const O_IMG_OPTM_WEBP_REPLACE_SRCSET = 'img_optm-webp_replace_srcset'; @@ -278,8 +286,15 @@ class Base extends Root { const O_QC_NAMESERVERS = 'qc-nameservers'; const O_QC_CNAME = 'qc-cname'; + // -------------------------------------------------- ## + // -------------- OptimaX ----------------- ## + // -------------------------------------------------- ## + const O_OPTIMAX = 'optimax'; + const NETWORK_O_USE_PRIMARY = 'use_primary_settings'; + const DEBUG_TMP_DISABLE = 'debug-disable_tmp'; + /*** Other consts ***/ const O_GUIDE = 'litespeed-guide'; // Array of each guidance tag as key, step as val //xx todo: may need to remove @@ -308,8 +323,12 @@ class Base extends Root { const IMG_OPTM_BM_EXIF = 8; // @Deprecated since v7.0 const IMG_OPTM_BM_AVIF = 16; // @Deprecated since v7.0 - /* Site related options (Will not overwrite other sites' config) */ - protected static $SINGLE_SITE_OPTIONS = array( + /** + * Site related options (Will not overwrite other sites' config). + * + * @var string[] + */ + protected static $single_site_options = [ self::O_CRAWLER, self::O_CRAWLER_SITEMAP, self::O_CDN, @@ -326,9 +345,14 @@ class Base extends Root { self::O_CDN_ATTR, self::O_QC_NAMESERVERS, self::O_QC_CNAME, - ); + ]; - protected static $_default_options = array( + /** + * Default options for single-site installs. + * + * @var array<string,mixed> + */ + protected static $_default_options = [ self::_VER => '', self::HASH => '', self::O_API_KEY => '', @@ -337,8 +361,8 @@ class Base extends Root { self::O_GUEST => false, self::O_GUEST_OPTM => false, self::O_NEWS => false, - self::O_GUEST_UAS => array(), - self::O_GUEST_IPS => array(), + self::O_GUEST_UAS => [], + self::O_GUEST_IPS => [], // Cache self::O_CACHE => false, @@ -347,30 +371,30 @@ class Base extends Root { self::O_CACHE_REST => false, self::O_CACHE_PAGE_LOGIN => false, self::O_CACHE_MOBILE => false, - self::O_CACHE_MOBILE_RULES => array(), + self::O_CACHE_MOBILE_RULES => [], self::O_CACHE_BROWSER => false, - self::O_CACHE_EXC_USERAGENTS => array(), - self::O_CACHE_EXC_COOKIES => array(), - self::O_CACHE_EXC_QS => array(), - self::O_CACHE_EXC_CAT => array(), - self::O_CACHE_EXC_TAG => array(), - self::O_CACHE_FORCE_URI => array(), - self::O_CACHE_FORCE_PUB_URI => array(), - self::O_CACHE_PRIV_URI => array(), - self::O_CACHE_EXC => array(), - self::O_CACHE_EXC_ROLES => array(), - self::O_CACHE_DROP_QS => array(), + self::O_CACHE_EXC_USERAGENTS => [], + self::O_CACHE_EXC_COOKIES => [], + self::O_CACHE_EXC_QS => [], + self::O_CACHE_EXC_CAT => [], + self::O_CACHE_EXC_TAG => [], + self::O_CACHE_FORCE_URI => [], + self::O_CACHE_FORCE_PUB_URI => [], + self::O_CACHE_PRIV_URI => [], + self::O_CACHE_EXC => [], + self::O_CACHE_EXC_ROLES => [], + self::O_CACHE_DROP_QS => [], self::O_CACHE_TTL_PUB => 0, self::O_CACHE_TTL_PRIV => 0, self::O_CACHE_TTL_FRONTPAGE => 0, self::O_CACHE_TTL_FEED => 0, self::O_CACHE_TTL_REST => 0, self::O_CACHE_TTL_BROWSER => 0, - self::O_CACHE_TTL_STATUS => array(), + self::O_CACHE_TTL_STATUS => [], self::O_CACHE_LOGIN_COOKIE => '', - self::O_CACHE_AJAX_TTL => array(), - self::O_CACHE_VARY_COOKIES => array(), - self::O_CACHE_VARY_GROUP => array(), + self::O_CACHE_AJAX_TTL => [], + self::O_CACHE_VARY_COOKIES => [], + self::O_CACHE_VARY_GROUP => [], // Purge self::O_PURGE_ON_UPGRADE => false, @@ -386,15 +410,15 @@ class Base extends Root { self::O_PURGE_POST_DATE => false, self::O_PURGE_POST_TERM => false, self::O_PURGE_POST_POSTTYPE => false, - self::O_PURGE_TIMED_URLS => array(), + self::O_PURGE_TIMED_URLS => [], self::O_PURGE_TIMED_URLS_TIME => '', - self::O_PURGE_HOOK_ALL => array(), + self::O_PURGE_HOOK_ALL => [], // ESI self::O_ESI => false, self::O_ESI_CACHE_ADMBAR => false, self::O_ESI_CACHE_COMMFORM => false, - self::O_ESI_NONCE => array(), + self::O_ESI_NONCE => [], // Util self::O_UTIL_INSTANT_CLICK => false, @@ -403,13 +427,13 @@ class Base extends Root { // Debug self::O_DEBUG_DISABLE_ALL => false, self::O_DEBUG => false, - self::O_DEBUG_IPS => array(), + self::O_DEBUG_IPS => [], self::O_DEBUG_LEVEL => false, self::O_DEBUG_FILESIZE => 0, self::O_DEBUG_COLLAPSE_QS => false, - self::O_DEBUG_INC => array(), - self::O_DEBUG_EXC => array(), - self::O_DEBUG_EXC_STRINGS => array(), + self::O_DEBUG_INC => [], + self::O_DEBUG_EXC => [], + self::O_DEBUG_EXC_STRINGS => [], // DB Optm self::O_DB_OPTM_REVISIONS_MAX => 0, @@ -421,39 +445,39 @@ class Base extends Root { self::O_OPTM_CSS_COMB_EXT_INL => false, self::O_OPTM_UCSS => false, self::O_OPTM_UCSS_INLINE => false, - self::O_OPTM_UCSS_SELECTOR_WHITELIST => array(), - self::O_OPTM_UCSS_FILE_EXC_INLINE => array(), - self::O_OPTM_UCSS_EXC => array(), - self::O_OPTM_CSS_EXC => array(), + self::O_OPTM_UCSS_SELECTOR_WHITELIST => [], + self::O_OPTM_UCSS_FILE_EXC_INLINE => [], + self::O_OPTM_UCSS_EXC => [], + self::O_OPTM_CSS_EXC => [], self::O_OPTM_JS_MIN => false, self::O_OPTM_JS_COMB => false, self::O_OPTM_JS_COMB_EXT_INL => false, - self::O_OPTM_JS_DELAY_INC => array(), - self::O_OPTM_JS_EXC => array(), + self::O_OPTM_JS_DELAY_INC => [], + self::O_OPTM_JS_EXC => [], self::O_OPTM_HTML_MIN => false, - self::O_OPTM_HTML_LAZY => array(), - self::O_OPTM_HTML_SKIP_COMMENTS => array(), + self::O_OPTM_HTML_LAZY => [], + self::O_OPTM_HTML_SKIP_COMMENTS => [], self::O_OPTM_QS_RM => false, self::O_OPTM_GGFONTS_RM => false, self::O_OPTM_CSS_ASYNC => false, self::O_OPTM_CCSS_PER_URL => false, - self::O_OPTM_CCSS_SEP_POSTTYPE => array(), - self::O_OPTM_CCSS_SEP_URI => array(), - self::O_OPTM_CCSS_SELECTOR_WHITELIST => array(), + self::O_OPTM_CCSS_SEP_POSTTYPE => [], + self::O_OPTM_CCSS_SEP_URI => [], + self::O_OPTM_CCSS_SELECTOR_WHITELIST => [], self::O_OPTM_CSS_ASYNC_INLINE => false, self::O_OPTM_CSS_FONT_DISPLAY => false, self::O_OPTM_JS_DEFER => false, self::O_OPTM_EMOJI_RM => false, self::O_OPTM_NOSCRIPT_RM => false, self::O_OPTM_GGFONTS_ASYNC => false, - self::O_OPTM_EXC_ROLES => array(), + self::O_OPTM_EXC_ROLES => [], self::O_OPTM_CCSS_CON => '', - self::O_OPTM_JS_DEFER_EXC => array(), - self::O_OPTM_GM_JS_EXC => array(), - self::O_OPTM_DNS_PREFETCH => array(), + self::O_OPTM_JS_DEFER_EXC => [], + self::O_OPTM_GM_JS_EXC => [], + self::O_OPTM_DNS_PREFETCH => [], self::O_OPTM_DNS_PREFETCH_CTRL => false, - self::O_OPTM_DNS_PRECONNECT => array(), - self::O_OPTM_EXC => array(), + self::O_OPTM_DNS_PRECONNECT => [], + self::O_OPTM_EXC => [], self::O_OPTM_GUEST_ONLY => false, // Object @@ -468,15 +492,15 @@ class Base extends Root { self::O_OBJECT_DB_ID => 0, self::O_OBJECT_USER => '', self::O_OBJECT_PSWD => '', - self::O_OBJECT_GLOBAL_GROUPS => array(), - self::O_OBJECT_NON_PERSISTENT_GROUPS => array(), + self::O_OBJECT_GLOBAL_GROUPS => [], + self::O_OBJECT_NON_PERSISTENT_GROUPS => [], // Discuss self::O_DISCUSS_AVATAR_CACHE => false, self::O_DISCUSS_AVATAR_CRON => false, self::O_DISCUSS_AVATAR_CACHE_TTL => 0, self::O_OPTM_LOCALIZE => false, - self::O_OPTM_LOCALIZE_DOMAINS => array(), + self::O_OPTM_LOCALIZE_DOMAINS => [], // Media self::O_MEDIA_LAZY => false, @@ -491,15 +515,16 @@ class Base extends Root { self::O_MEDIA_PLACEHOLDER_RESP_ASYNC => false, self::O_MEDIA_IFRAME_LAZY => false, self::O_MEDIA_ADD_MISSING_SIZES => false, - self::O_MEDIA_LAZY_EXC => array(), - self::O_MEDIA_LAZY_CLS_EXC => array(), - self::O_MEDIA_LAZY_PARENT_CLS_EXC => array(), - self::O_MEDIA_IFRAME_LAZY_CLS_EXC => array(), - self::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC => array(), - self::O_MEDIA_LAZY_URI_EXC => array(), - self::O_MEDIA_LQIP_EXC => array(), + self::O_MEDIA_LAZY_EXC => [], + self::O_MEDIA_LAZY_CLS_EXC => [], + self::O_MEDIA_LAZY_PARENT_CLS_EXC => [], + self::O_MEDIA_IFRAME_LAZY_CLS_EXC => [], + self::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC => [], + self::O_MEDIA_LAZY_URI_EXC => [], + self::O_MEDIA_LQIP_EXC => [], self::O_MEDIA_VPI => false, self::O_MEDIA_VPI_CRON => false, + self::O_MEDIA_AUTO_RESCALE_ORI => false, // Image Optm self::O_IMG_OPTM_AUTO => false, @@ -507,8 +532,9 @@ class Base extends Root { self::O_IMG_OPTM_RM_BKUP => false, self::O_IMG_OPTM_WEBP => false, self::O_IMG_OPTM_LOSSLESS => false, + self::O_IMG_OPTM_SIZES_SKIPPED => [], self::O_IMG_OPTM_EXIF => false, - self::O_IMG_OPTM_WEBP_ATTR => array(), + self::O_IMG_OPTM_WEBP_ATTR => [], self::O_IMG_OPTM_WEBP_REPLACE_SRCSET => false, self::O_IMG_OPTM_JPG_QUALITY => 0, @@ -517,8 +543,8 @@ class Base extends Root { self::O_CRAWLER_CRAWL_INTERVAL => 0, self::O_CRAWLER_LOAD_LIMIT => 0, self::O_CRAWLER_SITEMAP => '', - self::O_CRAWLER_ROLES => array(), - self::O_CRAWLER_COOKIES => array(), + self::O_CRAWLER_ROLES => [], + self::O_CRAWLER_COOKIES => [], // Misc self::O_MISC_HEARTBEAT_FRONT => false, @@ -530,9 +556,9 @@ class Base extends Root { // CDN self::O_CDN => false, - self::O_CDN_ORI => array(), - self::O_CDN_ORI_DIR => array(), - self::O_CDN_EXC => array(), + self::O_CDN_ORI => [], + self::O_CDN_ORI_DIR => [], + self::O_CDN_EXC => [], self::O_CDN_QUIC => false, self::O_CDN_CLOUDFLARE => false, self::O_CDN_CLOUDFLARE_EMAIL => '', @@ -540,14 +566,21 @@ class Base extends Root { self::O_CDN_CLOUDFLARE_NAME => '', self::O_CDN_CLOUDFLARE_ZONE => '', self::O_CDN_CLOUDFLARE_CLEAR => false, - self::O_CDN_MAPPING => array(), - self::O_CDN_ATTR => array(), + self::O_CDN_MAPPING => [], + self::O_CDN_ATTR => [], self::O_QC_NAMESERVERS => '', self::O_QC_CNAME => '', - ); - protected static $_default_site_options = array( + self::DEBUG_TMP_DISABLE => 0, + ]; + + /** + * Default options for multisite (site-level options stored network-wide). + * + * @var array<string,mixed> + */ + protected static $_default_site_options = [ self::_VER => '', self::O_CACHE => false, self::NETWORK_O_USE_PRIMARY => false, @@ -556,11 +589,11 @@ class Base extends Root { self::O_CACHE_BROWSER => false, self::O_CACHE_MOBILE => false, - self::O_CACHE_MOBILE_RULES => array(), + self::O_CACHE_MOBILE_RULES => [], self::O_CACHE_LOGIN_COOKIE => '', - self::O_CACHE_VARY_COOKIES => array(), - self::O_CACHE_EXC_COOKIES => array(), - self::O_CACHE_EXC_USERAGENTS => array(), + self::O_CACHE_VARY_COOKIES => [], + self::O_CACHE_EXC_COOKIES => [], + self::O_CACHE_EXC_USERAGENTS => [], self::O_CACHE_TTL_BROWSER => 0, self::O_PURGE_ON_UPGRADE => false, @@ -576,64 +609,74 @@ class Base extends Root { self::O_OBJECT_DB_ID => 0, self::O_OBJECT_USER => '', self::O_OBJECT_PSWD => '', - self::O_OBJECT_GLOBAL_GROUPS => array(), - self::O_OBJECT_NON_PERSISTENT_GROUPS => array(), + self::O_OBJECT_GLOBAL_GROUPS => [], + self::O_OBJECT_NON_PERSISTENT_GROUPS => [], // Debug self::O_DEBUG_DISABLE_ALL => false, self::O_DEBUG => false, - self::O_DEBUG_IPS => array(), + self::O_DEBUG_IPS => [], self::O_DEBUG_LEVEL => false, self::O_DEBUG_FILESIZE => 0, self::O_DEBUG_COLLAPSE_QS => false, - self::O_DEBUG_INC => array(), - self::O_DEBUG_EXC => array(), - self::O_DEBUG_EXC_STRINGS => array(), + self::O_DEBUG_INC => [], + self::O_DEBUG_EXC => [], + self::O_DEBUG_EXC_STRINGS => [], self::O_IMG_OPTM_WEBP => false, - ); + ]; - // NOTE: all the val of following items will be int while not bool - protected static $_multi_switch_list = array( + /** + * Multi-switch options: option ID => max state (int). + * NOTE: all the val of following items will be int while not bool + * + * @var array<string,int> + */ + protected static $_multi_switch_list = [ self::O_DEBUG => 2, self::O_OPTM_JS_DEFER => 2, self::O_IMG_OPTM_WEBP => 2, - ); + ]; /** - * Correct the option type + * Correct the option type. * * TODO: add similar network func * - * @since 3.0.3 + * @since 3.0.3 + * + * @param mixed $val Incoming value. + * @param string $id Option ID. + * @param bool $is_site_conf Whether using site-level defaults. + * @return mixed */ protected function type_casting( $val, $id, $is_site_conf = false ) { - $default_v = !$is_site_conf ? self::$_default_options[$id] : self::$_default_site_options[$id]; - if (is_bool($default_v)) { - if ($val === 'true') { + $default_v = ! $is_site_conf ? self::$_default_options[ $id ] : self::$_default_site_options[ $id ]; + if ( is_bool( $default_v ) ) { + if ( 'true' === $val ) { $val = true; } - if ($val === 'false') { + if ( 'false' === $val ) { $val = false; } - $max = $this->_conf_multi_switch($id); - if ($max) { + $max = $this->_conf_multi_switch( $id ); + if ( $max ) { $val = (int) $val; $val %= $max + 1; } else { $val = (bool) $val; } - } elseif (is_array($default_v)) { + } elseif ( is_array( $default_v ) ) { // from textarea input - if (!is_array($val)) { - $val = Utility::sanitize_lines($val, $this->_conf_filter($id)); + if ( ! is_array( $val ) ) { + $val = Utility::sanitize_lines( $val, $this->_conf_filter( $id ) ); } - } elseif (!is_string($default_v)) { + } elseif ( ! is_string( $default_v ) ) { $val = (int) $val; } else { // Check if the string has a limit set - $val = $this->_conf_string_val($id, $val); + $val = $this->_conf_string_val( $id, $val ); } return $val; @@ -642,29 +685,30 @@ protected function type_casting( $val, $id, $is_site_conf = false ) { /** * Load default network settings from data.ini * - * @since 3.0 + * @since 3.0 + * @return array<string,mixed> */ public function load_default_site_vals() { // Load network_default.json - if (file_exists(LSCWP_DIR . 'data/const.network_default.json')) { - $default_ini_cfg = json_decode(File::read(LSCWP_DIR . 'data/const.network_default.json'), true); - foreach (self::$_default_site_options as $k => $v) { - if (!array_key_exists($k, $default_ini_cfg)) { + if ( file_exists( LSCWP_DIR . 'data/const.network_default.json' ) ) { + $default_ini_cfg = json_decode( File::read( LSCWP_DIR . 'data/const.network_default.json' ), true ); + foreach ( self::$_default_site_options as $k => $v ) { + if ( ! array_key_exists( $k, $default_ini_cfg ) ) { continue; } // Parse value in ini file - $ini_v = $this->type_casting($default_ini_cfg[$k], $k, true); + $ini_v = $this->type_casting( $default_ini_cfg[ $k ], $k, true ); - if ($ini_v == $v) { + if ( $ini_v === $v ) { continue; } - self::$_default_site_options[$k] = $ini_v; + self::$_default_site_options[ $k ] = $ini_v; } } - self::$_default_site_options[self::_VER] = Core::VER; + self::$_default_site_options[ self::_VER ] = Core::VER; return self::$_default_site_options; } @@ -674,24 +718,25 @@ public function load_default_site_vals() { * * @since 3.0 * @access public + * @return array<string,mixed> */ public function load_default_vals() { // Load default.json - if (file_exists(LSCWP_DIR . 'data/const.default.json')) { - $default_ini_cfg = json_decode(File::read(LSCWP_DIR . 'data/const.default.json'), true); - foreach (self::$_default_options as $k => $v) { - if (!array_key_exists($k, $default_ini_cfg)) { + if ( file_exists( LSCWP_DIR . 'data/const.default.json' ) ) { + $default_ini_cfg = json_decode( File::read( LSCWP_DIR . 'data/const.default.json' ), true ); + foreach ( self::$_default_options as $k => $v ) { + if ( ! array_key_exists( $k, $default_ini_cfg ) ) { continue; } // Parse value in ini file - $ini_v = $this->type_casting($default_ini_cfg[$k], $k); + $ini_v = $this->type_casting( $default_ini_cfg[ $k ], $k ); // NOTE: Multiple lines value must be stored as array /** * Special handler for CDN_mapping * - * format in .ini: + * Format in .ini: * [cdn-mapping] * url[0] = 'https://example.com/' * inc_js[0] = true @@ -702,82 +747,91 @@ public function load_default_vals() { * format out: * [0] = [ 'url' => 'https://example.com', 'inc_js' => true, 'filetype' => [ '.css', '.js', '.jpg' ] ] */ - if ($k == self::O_CDN_MAPPING) { - $mapping_fields = array( + if ( self::O_CDN_MAPPING === $k ) { + $mapping_fields = [ self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE, // Array - ); - $ini_v2 = array(); - foreach ($ini_v[self::CDN_MAPPING_URL] as $k2 => $v2) { + ]; + $ini_v2 = []; + foreach ( $ini_v[ self::CDN_MAPPING_URL ] as $k2 => $v2 ) { // $k2 is numeric - $this_row = array(); - foreach ($mapping_fields as $v3) { - $this_v = !empty($ini_v[$v3][$k2]) ? $ini_v[$v3][$k2] : false; - if ($v3 == self::CDN_MAPPING_URL) { - $this_v = $this_v ?: ''; + $this_row = []; + foreach ( $mapping_fields as $v3 ) { + $this_v = ! empty( $ini_v[ $v3 ][ $k2 ] ) ? $ini_v[ $v3 ][ $k2 ] : false; + if ( self::CDN_MAPPING_URL === $v3 ) { + if ( empty( $this_v ) ) { + $this_v = ''; + } } - if ($v3 == self::CDN_MAPPING_FILETYPE) { - $this_v = $this_v ? Utility::sanitize_lines($this_v) : array(); // Note: Since v3.0 its already an array + if ( self::CDN_MAPPING_FILETYPE === $v3 ) { + $this_v = $this_v ? Utility::sanitize_lines( $this_v ) : []; // Note: Since v3.0 its already an array } - $this_row[$v3] = $this_v; + $this_row[ $v3 ] = $this_v; } - $ini_v2[$k2] = $this_row; + $ini_v2[ $k2 ] = $this_row; } $ini_v = $ini_v2; } - if ($ini_v == $v) { + if ( $ini_v === $v ) { continue; } - self::$_default_options[$k] = $ini_v; + self::$_default_options[ $k ] = $ini_v; } } // Load internal default vals // Setting the default bool to int is also to avoid type casting override it back to bool - self::$_default_options[self::O_CACHE] = is_multisite() ? self::VAL_ON2 : self::VAL_ON; // For multi site, default is 2 (Use Network Admin Settings). For single site, default is 1 (Enabled). + self::$_default_options[ self::O_CACHE ] = is_multisite() ? self::VAL_ON2 : self::VAL_ON; // For multi site, default is 2 (Use Network Admin Settings). For single site, default is 1 (Enabled). // Load default vals containing variables - if (!self::$_default_options[self::O_CDN_ORI_DIR]) { - self::$_default_options[self::O_CDN_ORI_DIR] = LSCWP_CONTENT_FOLDER . "\nwp-includes"; - self::$_default_options[self::O_CDN_ORI_DIR] = explode("\n", self::$_default_options[self::O_CDN_ORI_DIR]); - self::$_default_options[self::O_CDN_ORI_DIR] = array_map('trim', self::$_default_options[self::O_CDN_ORI_DIR]); + if ( ! self::$_default_options[ self::O_CDN_ORI_DIR ] ) { + self::$_default_options[ self::O_CDN_ORI_DIR ] = LSCWP_CONTENT_FOLDER . "\nwp-includes"; + self::$_default_options[ self::O_CDN_ORI_DIR ] = explode( "\n", self::$_default_options[ self::O_CDN_ORI_DIR ] ); + self::$_default_options[ self::O_CDN_ORI_DIR ] = array_map( 'trim', self::$_default_options[ self::O_CDN_ORI_DIR ] ); } // Set security key if not initialized yet - if (!self::$_default_options[self::HASH]) { - self::$_default_options[self::HASH] = Str::rrand(32); + if ( ! self::$_default_options[ self::HASH ] ) { + self::$_default_options[ self::HASH ] = Str::rrand( 32 ); } - self::$_default_options[self::_VER] = Core::VER; + self::$_default_options[ self::_VER ] = Core::VER; return self::$_default_options; } /** - * Format the string value + * Format the string value. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Option ID. + * @param mixed $val Value. + * @return string */ protected function _conf_string_val( $id, $val ) { - return $val; + return (string) $val; } /** - * If the switch setting is a triple value or not + * If the switch setting is a triple value or not. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Option ID. + * @return int|false */ protected function _conf_multi_switch( $id ) { - if (!empty(self::$_multi_switch_list[$id])) { - return self::$_multi_switch_list[$id]; + if ( ! empty( self::$_multi_switch_list[ $id ] ) ) { + return self::$_multi_switch_list[ $id ]; } - if ($id == self::O_CACHE && is_multisite()) { + if ( self::O_CACHE === $id && is_multisite() ) { return self::VAL_ON2; } @@ -785,30 +839,40 @@ protected function _conf_multi_switch( $id ) { } /** - * Append a new multi switch max limit for the bool option + * Append a new multi switch max limit for the bool option. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Option ID. + * @param int $v Max state. + * @return void */ public static function set_multi_switch( $id, $v ) { - self::$_multi_switch_list[$id] = $v; + self::$_multi_switch_list[ $id ] = $v; } /** - * Generate const name based on $id + * Generate const name based on $id. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Option ID. + * @return string */ public static function conf_const( $id ) { - return 'LITESPEED_CONF__' . strtoupper(str_replace('-', '__', $id)); + return 'LITESPEED_CONF__' . strtoupper( str_replace( '-', '__', $id ) ); } /** - * Filter to be used when saving setting + * Filter to be used when saving setting. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Option ID. + * @return string|false */ protected function _conf_filter( $id ) { - $filters = array( + $filters = [ self::O_MEDIA_LAZY_EXC => 'uri', self::O_DEBUG_INC => 'relative', self::O_DEBUG_EXC => 'relative', @@ -828,22 +892,25 @@ protected function _conf_filter( $id ) { // self::O_OPTM_LOCALIZE_DOMAINS => 'noprotocol', // `Localize Resources` // self:: => '', // self:: => '', - ); + ]; - if (!empty($filters[$id])) { - return $filters[$id]; + if ( ! empty( $filters[ $id ] ) ) { + return $filters[ $id ]; } return false; } /** - * If the setting changes worth a purge or not + * If the setting changes worth a purge or not. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Option ID. + * @return bool */ protected function _conf_purge( $id ) { - $check_ids = array( + $check_ids = [ self::O_MEDIA_LAZY_URI_EXC, self::O_OPTM_EXC, self::O_CACHE_PRIV_URI, @@ -851,68 +918,82 @@ protected function _conf_purge( $id ) { self::O_CACHE_FORCE_URI, self::O_CACHE_FORCE_PUB_URI, self::O_CACHE_EXC, - ); + ]; - return in_array($id, $check_ids); + return in_array( $id, $check_ids, true ); } /** - * If the setting changes worth a purge ALL or not + * If the setting changes worth a purge ALL or not. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Option ID. + * @return bool */ protected function _conf_purge_all( $id ) { - $check_ids = array( self::O_CACHE, self::O_ESI, self::O_DEBUG_DISABLE_ALL, self::NETWORK_O_USE_PRIMARY ); + $check_ids = [ self::O_CACHE, self::O_ESI, self::O_DEBUG_DISABLE_ALL, self::NETWORK_O_USE_PRIMARY ]; - return in_array($id, $check_ids); + return in_array( $id, $check_ids, true ); } /** - * If the setting is a pswd or not + * If the setting is a password or not. + * + * @since 3.0 * - * @since 3.0 + * @param string $id Option ID. + * @return bool */ protected function _conf_pswd( $id ) { - $check_ids = array( self::O_CDN_CLOUDFLARE_KEY, self::O_OBJECT_PSWD ); + $check_ids = [ self::O_CDN_CLOUDFLARE_KEY, self::O_OBJECT_PSWD ]; - return in_array($id, $check_ids); + return in_array( $id, $check_ids, true ); } /** - * If the setting is cron related or not + * If the setting is cron related or not. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Option ID. + * @return bool */ protected function _conf_cron( $id ) { - $check_ids = array( self::O_OPTM_CSS_ASYNC, self::O_MEDIA_PLACEHOLDER_RESP_ASYNC, self::O_DISCUSS_AVATAR_CRON, self::O_IMG_OPTM_AUTO, self::O_CRAWLER ); + $check_ids = [ self::O_OPTM_CSS_ASYNC, self::O_MEDIA_PLACEHOLDER_RESP_ASYNC, self::O_DISCUSS_AVATAR_CRON, self::O_IMG_OPTM_AUTO, self::O_CRAWLER ]; - return in_array($id, $check_ids); + return in_array( $id, $check_ids, true ); } /** - * If the setting changes worth a purge, return the tag + * If the setting changes worth a purge, return the tag. * - * @since 3.0 + * @since 3.0 + * + * @param string $id Option ID. + * @return string|false */ protected function _conf_purge_tag( $id ) { - $check_ids = array( + $check_ids = [ self::O_CACHE_PAGE_LOGIN => Tag::TYPE_LOGIN, - ); + ]; - if (!empty($check_ids[$id])) { - return $check_ids[$id]; + if ( ! empty( $check_ids[ $id ] ) ) { + return $check_ids[ $id ]; } return false; } /** - * Generate server vars + * Generate server vars. * * @since 2.4.1 + * + * @return array<string,mixed> Map of constant name => value|null. */ public function server_vars() { - $consts = array( + $consts = [ 'WP_SITEURL', 'WP_HOME', 'WP_CONTENT_DIR', @@ -927,10 +1008,10 @@ public function server_vars() { 'LITESPEED_ON', 'LSWCP_TAG_PREFIX', 'COOKIEHASH', - ); - $server_vars = array(); - foreach ($consts as $v) { - $server_vars[$v] = defined($v) ? constant($v) : null; + ]; + $server_vars = []; + foreach ( $consts as $v ) { + $server_vars[ $v ] = defined( $v ) ? constant( $v ) : null; } return $server_vars; diff --git a/src/cdn.cls.php b/src/cdn.cls.php index dbeb06ee6..9b6701d6a 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -1,96 +1,140 @@ <?php - /** - * The CDN class. + * CDN handling for LiteSpeed Cache. + * + * Rewrites eligible asset URLs to configured CDN endpoints and integrates with WordPress filters. * - * @since 1.2.3 - * @since 1.5 Moved into /inc - * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> + * @since 1.2.3 + * @package LiteSpeed */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class CDN + * + * Processes page content and WordPress asset URLs to map to CDN domains according to settings. + */ class CDN extends Root { + const LOG_TAG = '[CDN]'; const BYPASS = 'LITESPEED_BYPASS_CDN'; + /** + * The working HTML/content buffer being processed. + * + * @var string + */ private $content; + /** + * Whether CDN feature is enabled. + * + * @var bool + */ private $_cfg_cdn; + + /** + * List of original site URLs (may include wildcards) to be replaced. + * + * @var string[] + */ private $_cfg_url_ori; + + /** + * List of directories considered internal/original for CDN rewriting. + * + * @var string[] + */ private $_cfg_ori_dir; - private $_cfg_cdn_mapping = array(); + + /** + * CDN mapping rules; keys include mapping kinds or file extensions, values are URL(s). + * + * @var array<string,string|string[]> + */ + private $_cfg_cdn_mapping = []; + + /** + * List of URL substrings/regex used to exclude items from CDN. + * + * @var string[] + */ private $_cfg_cdn_exclude; - private $cdn_mapping_hosts = array(); + /** + * Hosts used by CDN mappings for quick membership checks. + * + * @var string[] + */ + private $cdn_mapping_hosts = []; /** - * Init + * Initialize CDN integration and register filters if enabled. * - * @since 1.2.3 + * @since 1.2.3 + * @return void */ public function init() { - Debug2::debug2('[CDN] init'); + self::debug2( 'init' ); - if (defined(self::BYPASS)) { - Debug2::debug2('CDN bypass'); + if ( defined( self::BYPASS ) ) { + self::debug2( 'CDN bypass' ); return; } - if (!Router::can_cdn()) { - if (!defined(self::BYPASS)) { - define(self::BYPASS, true); + if ( ! Router::can_cdn() ) { + if ( ! defined( self::BYPASS ) ) { + define( self::BYPASS, true ); } return; } - $this->_cfg_cdn = $this->conf(Base::O_CDN); - if (!$this->_cfg_cdn) { - if (!defined(self::BYPASS)) { - define(self::BYPASS, true); + $this->_cfg_cdn = $this->conf( Base::O_CDN ); + if ( ! $this->_cfg_cdn ) { + if ( ! defined( self::BYPASS ) ) { + define( self::BYPASS, true ); } return; } - $this->_cfg_url_ori = $this->conf(Base::O_CDN_ORI); + $this->_cfg_url_ori = $this->conf( Base::O_CDN_ORI ); // Parse cdn mapping data to array( 'filetype' => 'url' ) - $mapping_to_check = array( Base::CDN_MAPPING_INC_IMG, Base::CDN_MAPPING_INC_CSS, Base::CDN_MAPPING_INC_JS ); - foreach ($this->conf(Base::O_CDN_MAPPING) as $v) { - if (!$v[Base::CDN_MAPPING_URL]) { + $mapping_to_check = [ Base::CDN_MAPPING_INC_IMG, Base::CDN_MAPPING_INC_CSS, Base::CDN_MAPPING_INC_JS ]; + foreach ( $this->conf( Base::O_CDN_MAPPING ) as $v ) { + if ( ! $v[ Base::CDN_MAPPING_URL ] ) { continue; } - $this_url = $v[Base::CDN_MAPPING_URL]; - $this_host = parse_url($this_url, PHP_URL_HOST); + $this_url = $v[ Base::CDN_MAPPING_URL ]; + $this_host = wp_parse_url( $this_url, PHP_URL_HOST ); // Check img/css/js - foreach ($mapping_to_check as $to_check) { - if ($v[$to_check]) { - Debug2::debug2('[CDN] mapping ' . $to_check . ' -> ' . $this_url); + foreach ( $mapping_to_check as $to_check ) { + if ( $v[ $to_check ] ) { + self::debug2( 'mapping ' . $to_check . ' -> ' . $this_url ); // If filetype to url is one to many, make url be an array - $this->_append_cdn_mapping($to_check, $this_url); + $this->_append_cdn_mapping( $to_check, $this_url ); - if (!in_array($this_host, $this->cdn_mapping_hosts)) { + if ( ! in_array( $this_host, $this->cdn_mapping_hosts, true ) ) { $this->cdn_mapping_hosts[] = $this_host; } } } // Check file types - if ($v[Base::CDN_MAPPING_FILETYPE]) { - foreach ($v[Base::CDN_MAPPING_FILETYPE] as $v2) { - $this->_cfg_cdn_mapping[Base::CDN_MAPPING_FILETYPE] = true; + if ( $v[ Base::CDN_MAPPING_FILETYPE ] ) { + foreach ( $v[ Base::CDN_MAPPING_FILETYPE ] as $v2 ) { + $this->_cfg_cdn_mapping[ Base::CDN_MAPPING_FILETYPE ] = true; // If filetype to url is one to many, make url be an array - $this->_append_cdn_mapping($v2, $this_url); + $this->_append_cdn_mapping( $v2, $this_url ); - if (!in_array($this_host, $this->cdn_mapping_hosts)) { + if ( ! in_array( $this_host, $this->cdn_mapping_hosts, true ) ) { $this->cdn_mapping_hosts[] = $this_host; } } - Debug2::debug2('[CDN] mapping ' . implode(',', $v[Base::CDN_MAPPING_FILETYPE]) . ' -> ' . $this_url); + self::debug2( 'mapping ' . implode( ',', $v[ Base::CDN_MAPPING_FILETYPE ] ) . ' -> ' . $this_url ); } } @@ -112,71 +156,95 @@ public function init() { $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; } - if (!$this->_cfg_url_ori || !$this->_cfg_cdn_mapping) { - if (!defined(self::BYPASS)) { - define(self::BYPASS, true); + // Add IMAGES to rewrite if CDN Mapping setting is enabled + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG])) { + $add_images_type = apply_filters('litespeed_cdn_add_filetypes_image', array('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif')); + foreach ($add_images_type as $ext) { + $this->_cfg_cdn_mapping[$ext] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG]; + } + } + + // Add CSS to rewrite if CDN Mapping setting is enabled + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { + $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; + } + + // Add JS to rewrite if CDN Mapping setting is enabled + if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { + $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; + } + + if ( ! $this->_cfg_url_ori || ! $this->_cfg_cdn_mapping ) { + if ( ! defined( self::BYPASS ) ) { + define( self::BYPASS, true ); } return; } - $this->_cfg_ori_dir = $this->conf(Base::O_CDN_ORI_DIR); + $this->_cfg_ori_dir = $this->conf( Base::O_CDN_ORI_DIR ); // In case user customized upload path - if (defined('UPLOADS')) { + if ( defined( 'UPLOADS' ) ) { $this->_cfg_ori_dir[] = UPLOADS; } // Check if need preg_replace - $this->_cfg_url_ori = Utility::wildcard2regex($this->_cfg_url_ori); + $this->_cfg_url_ori = Utility::wildcard2regex( $this->_cfg_url_ori ); - $this->_cfg_cdn_exclude = $this->conf(Base::O_CDN_EXC); + $this->_cfg_cdn_exclude = $this->conf( Base::O_CDN_EXC ); - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG])) { + if ( ! empty( $this->_cfg_cdn_mapping[ Base::CDN_MAPPING_INC_IMG ] ) ) { // Hook to srcset - if (function_exists('wp_calculate_image_srcset')) { - add_filter('wp_calculate_image_srcset', array( $this, 'srcset' ), 999); + if ( function_exists( 'wp_calculate_image_srcset' ) ) { + add_filter( 'wp_calculate_image_srcset', [ $this, 'srcset' ], 999 ); } // Hook to mime icon - add_filter('wp_get_attachment_image_src', array( $this, 'attach_img_src' ), 999); - add_filter('wp_get_attachment_url', array( $this, 'url_img' ), 999); + add_filter( 'wp_get_attachment_image_src', [ $this, 'attach_img_src' ], 999 ); + add_filter( 'wp_get_attachment_url', [ $this, 'url_img' ], 999 ); } - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { - add_filter('style_loader_src', array( $this, 'url_css' ), 999); + if ( ! empty( $this->_cfg_cdn_mapping[ Base::CDN_MAPPING_INC_CSS ] ) ) { + add_filter( 'style_loader_src', [ $this, 'url_css' ], 999 ); } - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { - add_filter('script_loader_src', array( $this, 'url_js' ), 999); + if ( ! empty( $this->_cfg_cdn_mapping[ Base::CDN_MAPPING_INC_JS ] ) ) { + add_filter( 'script_loader_src', [ $this, 'url_js' ], 999 ); } - add_filter('litespeed_buffer_finalize', array( $this, 'finalize' ), 30); + add_filter( 'litespeed_buffer_finalize', [ $this, 'finalize' ], 30 ); } /** - * Associate all filetypes with url + * Associate all filetypes with CDN URL. * - * @since 2.0 + * @since 2.0 * @access private + * + * @param string $filetype Mapping key (e.g., extension or mapping constant). + * @param string $url CDN base URL to use for this mapping. + * @return void */ private function _append_cdn_mapping( $filetype, $url ) { // If filetype to url is one to many, make url be an array - if (empty($this->_cfg_cdn_mapping[$filetype])) { - $this->_cfg_cdn_mapping[$filetype] = $url; - } elseif (is_array($this->_cfg_cdn_mapping[$filetype])) { + if ( empty( $this->_cfg_cdn_mapping[ $filetype ] ) ) { + $this->_cfg_cdn_mapping[ $filetype ] = $url; + } elseif ( is_array( $this->_cfg_cdn_mapping[ $filetype ] ) ) { // Append url to filetype - $this->_cfg_cdn_mapping[$filetype][] = $url; + $this->_cfg_cdn_mapping[ $filetype ][] = $url; } else { // Convert _cfg_cdn_mapping from string to array - $this->_cfg_cdn_mapping[$filetype] = array( $this->_cfg_cdn_mapping[$filetype], $url ); + $this->_cfg_cdn_mapping[ $filetype ] = [ $this->_cfg_cdn_mapping[ $filetype ], $url ]; } } /** - * Run CDN process - * NOTE: As this is after cache finalized, can NOT set any cache control anymore + * Run CDN processing on finalized buffer. + * NOTE: After cache finalized, cannot change cache control. * - * @since 1.2.3 + * @since 1.2.3 * @access public - * @return string The content that is after optimization + * + * @param string $content The HTML/content buffer. + * @return string The processed content. */ public function finalize( $content ) { $this->content = $content; @@ -187,111 +255,117 @@ public function finalize( $content ) { } /** - * Replace CDN url + * Replace eligible URLs with CDN URLs in the working buffer. * - * @since 1.2.3 + * @since 1.2.3 * @access private + * @return void */ private function _finalize() { - if (defined(self::BYPASS)) { + if ( defined( self::BYPASS ) ) { return; } - Debug2::debug('CDN _finalize'); + self::debug( 'CDN _finalize' ); // Start replacing img src - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG])) { + if ( ! empty( $this->_cfg_cdn_mapping[ Base::CDN_MAPPING_INC_IMG ] ) ) { $this->_replace_img(); $this->_replace_inline_css(); } - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_FILETYPE])) { + if ( ! empty( $this->_cfg_cdn_mapping[ Base::CDN_MAPPING_FILETYPE ] ) ) { $this->_replace_file_types(); } } /** - * Parse all file types + * Parse all file types and replace according to configured attributes. * - * @since 1.2.3 + * @since 1.2.3 * @access private + * @return void */ private function _replace_file_types() { - $ele_to_check = $this->conf(Base::O_CDN_ATTR); + $ele_to_check = $this->conf( Base::O_CDN_ATTR ); - foreach ($ele_to_check as $v) { - if (!$v || strpos($v, '.') === false) { - Debug2::debug2('[CDN] replace setting bypassed: no . attribute ' . $v); + foreach ( $ele_to_check as $v ) { + if ( ! $v || false === strpos( $v, '.' ) ) { + self::debug2( 'replace setting bypassed: no . attribute ' . $v ); continue; } - Debug2::debug2('[CDN] replace attribute ' . $v); + self::debug2( 'replace attribute ' . $v ); - $v = explode('.', $v); - $attr = preg_quote($v[1], '#'); - if ($v[0]) { - $pattern = '#<' . preg_quote($v[0], '#') . '([^>]+)' . $attr . '=([\'"])(.+)\g{2}#iU'; + $v = explode( '.', $v ); + $attr = preg_quote( $v[1], '#' ); + if ( $v[0] ) { + $pattern = '#<' . preg_quote( $v[0], '#' ) . '([^>]+)' . $attr . '=([\'"])(.+)\g{2}#iU'; } else { $pattern = '# ' . $attr . '=([\'"])(.+)\g{1}#iU'; } - preg_match_all($pattern, $this->content, $matches); + preg_match_all( $pattern, $this->content, $matches ); if (empty($matches[$v[0] ? 3 : 2])) { continue; } foreach ($matches[$v[0] ? 3 : 2] as $k2 => $url) { - // Debug2::debug2( '[CDN] check ' . $url ); - $postfix = '.' . pathinfo((string) parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION); + // self::debug2( 'check ' . $url ); + $postfix = '.' . pathinfo((string) wp_parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION); if (!array_key_exists($postfix, $this->_cfg_cdn_mapping)) { - // Debug2::debug2( '[CDN] non-existed postfix ' . $postfix ); + // self::debug2( 'non-existed postfix ' . $postfix ); continue; } - Debug2::debug2('[CDN] matched file_type ' . $postfix . ' : ' . $url); + self::debug2( 'matched file_type ' . $postfix . ' : ' . $url ); - if (!($url2 = $this->rewrite($url, Base::CDN_MAPPING_FILETYPE, $postfix))) { + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_FILETYPE, $postfix ); + if ( ! $url2 ) { continue; } - $attr = str_replace($url, $url2, $matches[0][$k2]); - $this->content = str_replace($matches[0][$k2], $attr, $this->content); + $attr_str = str_replace( $url, $url2, $matches[0][ $k2 ] ); + $this->content = str_replace( $matches[0][ $k2 ], $attr_str, $this->content ); } } } /** - * Parse all images + * Parse all images and replace their src attributes. * - * @since 1.2.3 + * @since 1.2.3 * @access private + * @return void */ private function _replace_img() { - preg_match_all('#<img([^>]+?)src=([\'"\\\]*)([^\'"\s\\\>]+)([\'"\\\]*)([^>]*)>#i', $this->content, $matches); - foreach ($matches[3] as $k => $url) { + preg_match_all( '#<img([^>]+?)src=([\'"\\\]*)([^\'"\s\\\>]+)([\'"\\\]*)([^>]*)>#i', $this->content, $matches ); + foreach ( $matches[3] as $k => $url ) { // Check if is a DATA-URI - if (strpos($url, 'data:image') !== false) { + if ( false !== strpos( $url, 'data:image' ) ) { continue; } - if (!($url2 = $this->rewrite($url, Base::CDN_MAPPING_INC_IMG))) { + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_IMG ); + if ( ! $url2 ) { continue; } - $html_snippet = sprintf('<img %1$s src=%2$s %3$s>', $matches[1][$k], $matches[2][$k] . $url2 . $matches[4][$k], $matches[5][$k]); - $this->content = str_replace($matches[0][$k], $html_snippet, $this->content); + $html_snippet = sprintf( '<img %1$s src=%2$s %3$s>', $matches[1][ $k ], $matches[2][ $k ] . $url2 . $matches[4][ $k ], $matches[5][ $k ] ); + $this->content = str_replace( $matches[0][ $k ], $html_snippet, $this->content ); } } /** - * Parse and replace all inline styles containing url() + * Parse and replace all inline styles containing url(). * - * @since 1.2.3 + * @since 1.2.3 * @access private + * @return void */ private function _replace_inline_css() { - Debug2::debug2('[CDN] _replace_inline_css', $this->_cfg_cdn_mapping); + self::debug2( '_replace_inline_css', $this->_cfg_cdn_mapping ); /** * Excludes `\` from URL matching @@ -300,211 +374,243 @@ private function _replace_inline_css() { * @see #685485 * @since 3.0 */ - preg_match_all('/url\((?![\'"]?data)[\'"]?(.+?)[\'"]?\)/i', $this->content, $matches); - foreach ($matches[1] as $k => $url) { - $url = str_replace(array( ' ', '\t', '\n', '\r', '\0', '\x0B', '"', "'", '"', ''' ), '', $url); + preg_match_all( '/url\((?![\'"]?data)[\'"]?(.+?)[\'"]?\)/i', $this->content, $matches ); + foreach ( $matches[1] as $k => $url ) { + $url = str_replace( [ ' ', '\t', '\n', '\r', '\0', '\x0B', '"', "'", '"', ''' ], '', $url ); // Parse file postfix - $parsed_url = parse_url($url, PHP_URL_PATH); - if (!$parsed_url) { + $parsed_url = wp_parse_url( $url, PHP_URL_PATH ); + if ( ! $parsed_url ) { continue; } - $postfix = '.' . pathinfo($parsed_url, PATHINFO_EXTENSION); - if (array_key_exists($postfix, $this->_cfg_cdn_mapping)) { - Debug2::debug2('[CDN] matched file_type ' . $postfix . ' : ' . $url); - if (!($url2 = $this->rewrite($url, Base::CDN_MAPPING_FILETYPE, $postfix))) { + $postfix = '.' . pathinfo( $parsed_url, PATHINFO_EXTENSION ); + if ( array_key_exists( $postfix, $this->_cfg_cdn_mapping ) ) { + self::debug2( 'matched file_type ' . $postfix . ' : ' . $url ); + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_FILETYPE, $postfix ); + if ( ! $url2 ) { continue; } - } elseif (in_array($postfix, array( 'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'avif' ))) { - if (!($url2 = $this->rewrite($url, Base::CDN_MAPPING_INC_IMG))) { + } elseif ( in_array( $postfix, [ 'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'avif' ], true ) ) { + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_IMG ); + if ( ! $url2 ) { continue; } } else { continue; } - $attr = str_replace($matches[1][$k], $url2, $matches[0][$k]); - $this->content = str_replace($matches[0][$k], $attr, $this->content); + $attr = str_replace( $matches[1][ $k ], $url2, $matches[0][ $k ] ); + $this->content = str_replace( $matches[0][ $k ], $attr, $this->content ); } - Debug2::debug2('[CDN] _replace_inline_css done'); + self::debug2( '_replace_inline_css done' ); } /** - * Hook to wp_get_attachment_image_src + * Filter: wp_get_attachment_image_src. * - * @since 1.2.3 - * @since 1.7 Removed static from function + * @since 1.2.3 + * @since 1.7 Removed static from function. * @access public - * @param array $img The URL of the attachment image src, the width, the height - * @return array + * + * @param array{0:string,1:int,2:int} $img The URL of the attachment image src, the width, the height. + * @return array{0:string,1:int,2:int} */ public function attach_img_src( $img ) { - if ($img && ($url = $this->rewrite($img[0], Base::CDN_MAPPING_INC_IMG))) { - $img[0] = $url; + if ( $img ) { + $url = $this->rewrite( $img[0], Base::CDN_MAPPING_INC_IMG ); + if ( $url ) { + $img[0] = $url; + } } return $img; } /** - * Try to rewrite one URL with CDN + * Try to rewrite one image URL with CDN. * - * @since 1.7 + * @since 1.7 * @access public + * + * @param string $url Original URL. + * @return string URL after rewriting, or original if not applicable. */ public function url_img( $url ) { - if ($url && ($url2 = $this->rewrite($url, Base::CDN_MAPPING_INC_IMG))) { - $url = $url2; + if ( $url ) { + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_IMG ); + if ( $url2 ) { + $url = $url2; + } } return $url; } /** - * Try to rewrite one URL with CDN + * Try to rewrite one CSS URL with CDN. * - * @since 1.7 + * @since 1.7 * @access public + * + * @param string $url Original URL. + * @return string URL after rewriting, or original if not applicable. */ public function url_css( $url ) { - if ($url && ($url2 = $this->rewrite($url, Base::CDN_MAPPING_INC_CSS))) { - $url = $url2; + if ( $url ) { + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_CSS ); + if ( $url2 ) { + $url = $url2; + } } return $url; } /** - * Try to rewrite one URL with CDN + * Try to rewrite one JS URL with CDN. * - * @since 1.7 + * @since 1.7 * @access public + * + * @param string $url Original URL. + * @return string URL after rewriting, or original if not applicable. */ public function url_js( $url ) { - if ($url && ($url2 = $this->rewrite($url, Base::CDN_MAPPING_INC_JS))) { - $url = $url2; + if ( $url ) { + $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_JS ); + if ( $url2 ) { + $url = $url2; + } } return $url; } /** - * Hook to replace WP responsive images + * Filter responsive image sources for CDN. * - * @since 1.2.3 - * @since 1.7 Removed static from function + * @since 1.2.3 + * @since 1.7 Removed static from function. * @access public - * @param array $srcs - * @return array + * + * @param array<int,array{url:string}> $srcs Srcset array. + * @return array<int,array{url:string}> */ public function srcset( $srcs ) { - if ($srcs) { - foreach ($srcs as $w => $data) { - if (!($url = $this->rewrite($data['url'], Base::CDN_MAPPING_INC_IMG))) { + if ( $srcs ) { + foreach ( $srcs as $w => $data ) { + $url = $this->rewrite( $data['url'], Base::CDN_MAPPING_INC_IMG ); + if ( ! $url ) { continue; } - $srcs[$w]['url'] = $url; + $srcs[ $w ]['url'] = $url; } } return $srcs; } /** - * Replace URL to CDN URL + * Replace an URL with mapped CDN URL, if applicable. * - * @since 1.2.3 + * @since 1.2.3 * @access public - * @param string $url - * @return string Replaced URL + * + * @param string $url Target URL. + * @param string $mapping_kind Mapping kind (e.g., Base::CDN_MAPPING_INC_IMG or Base::CDN_MAPPING_FILETYPE). + * @param string|false $postfix File extension (with dot) when mapping by file type. + * @return string|false Replaced URL on success, false when not applicable. */ public function rewrite( $url, $mapping_kind, $postfix = false ) { - Debug2::debug2('[CDN] rewrite ' . $url); - $url_parsed = parse_url($url); + self::debug2( 'rewrite ' . $url ); + $url_parsed = wp_parse_url( $url ); - if (empty($url_parsed['path'])) { - Debug2::debug2('[CDN] -rewrite bypassed: no path'); + if ( empty( $url_parsed['path'] ) ) { + self::debug2( '-rewrite bypassed: no path' ); return false; } - // Only images under wp-cotnent/wp-includes can be replaced - $is_internal_folder = Utility::str_hit_array($url_parsed['path'], $this->_cfg_ori_dir); - if (!$is_internal_folder) { - Debug2::debug2('[CDN] -rewrite failed: path not match: ' . LSCWP_CONTENT_FOLDER); + // Only images under wp-content/wp-includes can be replaced + $is_internal_folder = Utility::str_hit_array( $url_parsed['path'], $this->_cfg_ori_dir ); + if ( ! $is_internal_folder ) { + self::debug2( '-rewrite failed: path not match: ' . LSCWP_CONTENT_FOLDER ); return false; } // Check if is external url - if (!empty($url_parsed['host'])) { - if (!Utility::internal($url_parsed['host']) && !$this->_is_ori_url($url)) { - Debug2::debug2('[CDN] -rewrite failed: host not internal'); + if ( ! empty( $url_parsed['host'] ) ) { + if ( ! Utility::internal( $url_parsed['host'] ) && ! $this->_is_ori_url( $url ) ) { + self::debug2( '-rewrite failed: host not internal' ); return false; } } - $exclude = Utility::str_hit_array($url, $this->_cfg_cdn_exclude); - if ($exclude) { - Debug2::debug2('[CDN] -abort excludes ' . $exclude); + $exclude = Utility::str_hit_array( $url, $this->_cfg_cdn_exclude ); + if ( $exclude ) { + self::debug2( '-abort excludes ' . $exclude ); return false; } // Fill full url before replacement - if (empty($url_parsed['host'])) { - $url = Utility::uri2url($url); - Debug2::debug2('[CDN] -fill before rewritten: ' . $url); + if ( empty( $url_parsed['host'] ) ) { + $url = Utility::uri2url( $url ); + self::debug2( '-fill before rewritten: ' . $url ); - $url_parsed = parse_url($url); + $url_parsed = wp_parse_url( $url ); } - $scheme = !empty($url_parsed['scheme']) ? $url_parsed['scheme'] . ':' : ''; - if ($scheme) { - // Debug2::debug2( '[CDN] -scheme from url: ' . $scheme ); - } + $scheme = ! empty( $url_parsed['scheme'] ) ? $url_parsed['scheme'] . ':' : ''; // Find the mapping url to be replaced to - if (empty($this->_cfg_cdn_mapping[$mapping_kind])) { + if ( empty( $this->_cfg_cdn_mapping[ $mapping_kind ] ) ) { return false; } - if ($mapping_kind !== Base::CDN_MAPPING_FILETYPE) { - $final_url = $this->_cfg_cdn_mapping[$mapping_kind]; + if ( Base::CDN_MAPPING_FILETYPE !== $mapping_kind ) { + $final_url = $this->_cfg_cdn_mapping[ $mapping_kind ]; } else { // select from file type - $final_url = $this->_cfg_cdn_mapping[$postfix]; + $final_url = $this->_cfg_cdn_mapping[ $postfix ]; + if ( ! $final_url ) { + return false; + } } // If filetype to url is one to many, need to random one - if (is_array($final_url)) { - $final_url = $final_url[array_rand($final_url)]; + if ( is_array( $final_url ) ) { + $final_url = $final_url[ array_rand( $final_url ) ]; } // Now lets replace CDN url - foreach ($this->_cfg_url_ori as $v) { - if (strpos($v, '*') !== false) { - $url = preg_replace('#' . $scheme . $v . '#iU', $final_url, $url); + foreach ( $this->_cfg_url_ori as $v ) { + if ( false !== strpos( $v, '*' ) ) { + $url = preg_replace( '#' . $scheme . $v . '#iU', $final_url, $url ); } else { - $url = str_replace($scheme . $v, $final_url, $url); + $url = str_replace( $scheme . $v, $final_url, $url ); } } - Debug2::debug2('[CDN] -rewritten: ' . $url); + self::debug2( '-rewritten: ' . $url ); return $url; } /** - * Check if is original URL of CDN or not + * Check if the given URL matches any configured "original" URLs for CDN. * - * @since 2.1 + * @since 2.1 * @access private + * + * @param string $url URL to test. + * @return bool True if URL is one of the originals. */ private function _is_ori_url( $url ) { - $url_parsed = parse_url($url); + $url_parsed = wp_parse_url( $url ); - $scheme = !empty($url_parsed['scheme']) ? $url_parsed['scheme'] . ':' : ''; + $scheme = ! empty( $url_parsed['scheme'] ) ? $url_parsed['scheme'] . ':' : ''; - foreach ($this->_cfg_url_ori as $v) { + foreach ( $this->_cfg_url_ori as $v ) { $needle = $scheme . $v; - if (strpos($v, '*') !== false) { - if (preg_match('#' . $needle . '#iU', $url)) { + if ( false !== strpos( $v, '*' ) ) { + if ( preg_match( '#' . $needle . '#iU', $url ) ) { return true; } - } elseif (strpos($url, $needle) === 0) { + } elseif ( 0 === strpos( $url, $needle ) ) { return true; } } @@ -513,17 +619,20 @@ private function _is_ori_url( $url ) { } /** - * Check if the host is the CDN internal host + * Check if the host is one of the CDN mapping hosts. + * + * @since 1.2.3 * - * @since 1.2.3 + * @param string $host Hostname to check. + * @return bool False when bypassed, otherwise true if internal CDN host. */ public static function internal( $host ) { - if (defined(self::BYPASS)) { + if ( defined( self::BYPASS ) ) { return false; } $instance = self::cls(); - return in_array($host, $instance->cdn_mapping_hosts); // todo: can add $this->_is_ori_url() check in future + return in_array( $host, $instance->cdn_mapping_hosts, true ); // todo: can add $this->_is_ori_url() check in future } } diff --git a/src/cdn/cloudflare.cls.php b/src/cdn/cloudflare.cls.php index 1679f814e..d7340c31b 100644 --- a/src/cdn/cloudflare.cls.php +++ b/src/cdn/cloudflare.cls.php @@ -7,9 +7,9 @@ * @subpackage LiteSpeed/src/cdn * @author LiteSpeed Technologies <info@litespeedtech.com> */ + namespace LiteSpeed\CDN; -use LiteSpeed\Core; use LiteSpeed\Base; use LiteSpeed\Debug2; use LiteSpeed\Router; @@ -18,6 +18,11 @@ defined('WPINC') || exit(); +/** + * Class Cloudflare + * + * @since 2.1 + */ class Cloudflare extends Base { const TYPE_PURGE_ALL = 'purge_all'; @@ -38,13 +43,13 @@ public function try_refresh_zone() { return; } - $zone = $this->_fetch_zone(); + $zone = $this->fetch_zone(); if ($zone) { $this->cls('Conf')->update(self::O_CDN_CLOUDFLARE_NAME, $zone['name']); $this->cls('Conf')->update(self::O_CDN_CLOUDFLARE_ZONE, $zone['id']); - Debug2::debug("[Cloudflare] Get zone successfully \t\t[ID] $zone[id]"); + Debug2::debug("[Cloudflare] Get zone successfully \t\t[ID] " . $zone['id']); } else { $this->cls('Conf')->update(self::O_CDN_CLOUDFLARE_ZONE, ''); Debug2::debug('[Cloudflare] ❌ Get zone failed, clean zone'); @@ -56,25 +61,29 @@ public function try_refresh_zone() { * * @since 1.7.2 * @access private + * @param bool $show_msg Whether to show success/error message. */ - private function _get_devmode( $show_msg = true ) { - Debug2::debug('[Cloudflare] _get_devmode'); + private function get_devmode( $show_msg = true ) { + Debug2::debug('[Cloudflare] get_devmode'); - $zone = $this->_zone(); + $zone = $this->zone(); if (!$zone) { return; } $url = 'https://api.cloudflare.com/client/v4/zones/' . $zone . '/settings/development_mode'; - $res = $this->_cloudflare_call($url, 'GET', false, $show_msg); + $res = $this->cloudflare_call($url, 'GET', false, $show_msg); if (!$res) { return; } - Debug2::debug('[Cloudflare] _get_devmode result ', $res); + Debug2::debug('[Cloudflare] get_devmode result ', $res); // Make sure is array: #992174 - $curr_status = self::get_option(self::ITEM_STATUS, array()) ?: array(); + $curr_status = self::get_option(self::ITEM_STATUS, array()); + if ( ! is_array( $curr_status ) ) { + $curr_status = array(); + } $curr_status['devmode'] = $res['value']; $curr_status['devmode_expired'] = $res['time_remaining'] + time(); @@ -87,25 +96,26 @@ private function _get_devmode( $show_msg = true ) { * * @since 1.7.2 * @access private + * @param string $type The type of development mode to set (on/off). */ - private function _set_devmode( $type ) { - Debug2::debug('[Cloudflare] _set_devmode'); + private function set_devmode( $type ) { + Debug2::debug('[Cloudflare] set_devmode'); - $zone = $this->_zone(); + $zone = $this->zone(); if (!$zone) { return; } $url = 'https://api.cloudflare.com/client/v4/zones/' . $zone . '/settings/development_mode'; - $new_val = $type == self::TYPE_SET_DEVMODE_ON ? 'on' : 'off'; + $new_val = self::TYPE_SET_DEVMODE_ON === $type ? 'on' : 'off'; $data = array( 'value' => $new_val ); - $res = $this->_cloudflare_call($url, 'PATCH', $data); + $res = $this->cloudflare_call($url, 'PATCH', $data); if (!$res) { return; } - $res = $this->_get_devmode(false); + $res = $this->get_devmode(false); if ($res) { $msg = sprintf(__('Notified Cloudflare to set development mode to %s successfully.', 'litespeed-cache'), strtoupper($new_val)); @@ -117,12 +127,14 @@ private function _set_devmode( $type ) { * Shortcut to purge Cloudflare * * @since 7.1 + * @access public + * @param string|bool $reason The reason for purging, or false if none. */ public static function purge_all( $reason = false ) { if ($reason) { Debug2::debug('[Cloudflare] purge call because: ' . $reason); } - self::cls()->_purge_all(); + self::cls()->purge_all_private(); } /** @@ -131,8 +143,8 @@ public static function purge_all( $reason = false ) { * @since 1.7.2 * @access private */ - private function _purge_all() { - Debug2::debug('[Cloudflare] _purge_all'); + private function purge_all_private() { + Debug2::debug('[Cloudflare] purge_all_private'); $cf_on = $this->conf(self::O_CDN_CLOUDFLARE); if (!$cf_on) { @@ -141,7 +153,7 @@ private function _purge_all() { return; } - $zone = $this->_zone(); + $zone = $this->zone(); if (!$zone) { return; } @@ -149,7 +161,7 @@ private function _purge_all() { $url = 'https://api.cloudflare.com/client/v4/zones/' . $zone . '/purge_cache'; $data = array( 'purge_everything' => true ); - $res = $this->_cloudflare_call($url, 'DELETE', $data); + $res = $this->cloudflare_call($url, 'DELETE', $data); if ($res) { $msg = __('Notified Cloudflare to purge all successfully.', 'litespeed-cache'); @@ -163,7 +175,7 @@ private function _purge_all() { * @since 1.7.2 * @access private */ - private function _zone() { + private function zone() { $zone = $this->conf(self::O_CDN_CLOUDFLARE_ZONE); if (!$zone) { $msg = __('No available Cloudflare zone', 'litespeed-cache'); @@ -180,14 +192,14 @@ private function _zone() { * @since 1.7.2 * @access private */ - private function _fetch_zone() { + private function fetch_zone() { $kw = $this->conf(self::O_CDN_CLOUDFLARE_NAME); $url = 'https://api.cloudflare.com/client/v4/zones?status=active&match=all'; // Try exact match first - if ($kw && strpos($kw, '.')) { - $zones = $this->_cloudflare_call($url . '&name=' . $kw, 'GET', false, false); + if ($kw && false !== strpos($kw, '.')) { + $zones = $this->cloudflare_call($url . '&name=' . $kw, 'GET', false, false); if ($zones) { Debug2::debug('[Cloudflare] fetch_zone exact matched'); return $zones[0]; @@ -195,7 +207,7 @@ private function _fetch_zone() { } // Can't find, try to get default one - $zones = $this->_cloudflare_call($url, 'GET', false, false); + $zones = $this->cloudflare_call($url, 'GET', false, false); if (!$zones) { Debug2::debug('[Cloudflare] fetch_zone no zone'); @@ -208,7 +220,7 @@ private function _fetch_zone() { } foreach ($zones as $v) { - if (strpos($v['name'], $kw) !== false) { + if (false !== strpos($v['name'], $kw)) { Debug2::debug('[Cloudflare] fetch_zone matched ' . $kw . ' [name] ' . $v['name']); return $v; } @@ -224,31 +236,35 @@ private function _fetch_zone() { * * @since 1.7.2 * @access private + * @param string $url The API URL to call. + * @param string $method The HTTP method to use (GET, POST, etc.). + * @param array|bool $data The data to send with the request, or false if none. + * @param bool $show_msg Whether to show success/error message. */ - private function _cloudflare_call( $url, $method = 'GET', $data = false, $show_msg = true ) { - Debug2::debug("[Cloudflare] _cloudflare_call \t\t[URL] $url"); + private function cloudflare_call( $url, $method = 'GET', $data = false, $show_msg = true ) { + Debug2::debug("[Cloudflare] cloudflare_call \t\t[URL] $url"); - if (40 == strlen($this->conf(self::O_CDN_CLOUDFLARE_KEY))) { + if (strlen($this->conf(self::O_CDN_CLOUDFLARE_KEY)) === 40) { $headers = array( - 'Content-Type' => 'application/json', + 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $this->conf(self::O_CDN_CLOUDFLARE_KEY), ); } else { $headers = array( - 'Content-Type' => 'application/json', + 'Content-Type' => 'application/json', 'X-Auth-Email' => $this->conf(self::O_CDN_CLOUDFLARE_EMAIL), - 'X-Auth-Key' => $this->conf(self::O_CDN_CLOUDFLARE_KEY), + 'X-Auth-Key' => $this->conf(self::O_CDN_CLOUDFLARE_KEY), ); } $wp_args = array( - 'method' => $method, + 'method' => $method, 'headers' => $headers, ); if ($data) { if (is_array($data)) { - $data = \json_encode($data); + $data = wp_json_encode($data); } $wp_args['body'] = $data; } @@ -267,7 +283,7 @@ private function _cloudflare_call( $url, $method = 'GET', $data = false, $show_m $json = \json_decode($result, true); if ($json && $json['success'] && $json['result']) { - Debug2::debug('[Cloudflare] _cloudflare_call called successfully'); + Debug2::debug('[Cloudflare] cloudflare_call called successfully'); if ($show_msg) { $msg = __('Communicated with Cloudflare successfully.', 'litespeed-cache'); Admin_Display::success($msg); @@ -276,7 +292,7 @@ private function _cloudflare_call( $url, $method = 'GET', $data = false, $show_m return $json['result']; } - Debug2::debug("[Cloudflare] _cloudflare_call called failed: $result"); + Debug2::debug("[Cloudflare] cloudflare_call called failed: $result"); if ($show_msg) { $msg = __('Failed to communicate with Cloudflare', 'litespeed-cache'); Admin_Display::error($msg); @@ -296,16 +312,16 @@ public function handler() { switch ($type) { case self::TYPE_PURGE_ALL: - $this->_purge_all(); + $this->purge_all_private(); break; case self::TYPE_GET_DEVMODE: - $this->_get_devmode(); + $this->get_devmode(); break; case self::TYPE_SET_DEVMODE_ON: case self::TYPE_SET_DEVMODE_OFF: - $this->_set_devmode($type); + $this->set_devmode($type); break; default: diff --git a/src/cdn/quic.cls.php b/src/cdn/quic.cls.php index 769a6dc1e..de4393823 100644 --- a/src/cdn/quic.cls.php +++ b/src/cdn/quic.cls.php @@ -1,12 +1,10 @@ <?php - /** * The quic.cloud class. * * @since 2.4.1 * @package LiteSpeed * @subpackage LiteSpeed/src/cdn - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed\CDN; @@ -16,32 +14,44 @@ defined('WPINC') || exit(); +/** + * Class Quic + * + * Handles Quic.cloud CDN integration. + * + * @since 2.4.1 + */ class Quic extends Base { - - const LOG_TAG = '☁️'; - + const LOG_TAG = '☁️'; const TYPE_REG = 'reg'; - protected $_summary; - private $_force = false; - public function __construct() { - $this->_summary = self::get_summary(); - } + /** + * Force sync flag. + * + * @var bool + */ + private $force = false; /** * Notify CDN new config updated * + * Syncs configuration with Quic.cloud CDN. + * + * @since 2.4.1 * @access public + * @param bool $force Whether to force sync. + * @return bool|void */ public function try_sync_conf( $force = false ) { + $cloud_summary = Cloud::get_summary(); if ($force) { - $this->_force = $force; + $this->force = $force; } if (!$this->conf(self::O_CDN_QUIC)) { - if (!empty($this->_summary['conf_md5'])) { + if (!empty($cloud_summary['conf_md5'])) { self::debug('❌ No QC CDN, clear conf md5!'); - self::save_summary(array( 'conf_md5' => '' )); + Cloud::save_summary(array( 'conf_md5' => '' )); } return false; } @@ -93,20 +103,20 @@ public function try_sync_conf( $force = false ) { } } - $conf_md5 = md5(\json_encode($options_for_md5)); - if (!empty($this->_summary['conf_md5'])) { - if ($conf_md5 == $this->_summary['conf_md5']) { - if (!$this->_force) { + $conf_md5 = md5(wp_json_encode($options_for_md5)); + if (!empty($cloud_summary['conf_md5'])) { + if ($conf_md5 === $cloud_summary['conf_md5']) { + if (!$this->force) { self::debug('Bypass sync conf to QC due to same md5', $conf_md5); return; } self::debug('!!!Force sync conf even same md5'); } else { - self::debug('[conf_md5] ' . $conf_md5 . ' [existing_conf_md5] ' . $this->_summary['conf_md5']); + self::debug('[conf_md5] ' . $conf_md5 . ' [existing_conf_md5] ' . $cloud_summary['conf_md5']); } } - self::save_summary(array( 'conf_md5' => $conf_md5 )); + Cloud::save_summary(array( 'conf_md5' => $conf_md5 )); self::debug('sync conf to QC'); Cloud::post(Cloud::SVC_D_SYNC_CONF, $options_for_md5); diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 5e8752009..078627336 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -1,22 +1,51 @@ <?php - /** * Cloud service cls * - * @since 3.0 + * @package LiteSpeed + * @since 3.0 */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Cloud + * + * Handles QUIC.cloud communication, node detection, activation, and related utilities. + */ class Cloud extends Base { - const LOG_TAG = '❄️'; - const CLOUD_SERVER = 'https://api.quic.cloud'; - const CLOUD_IPS = 'https://quic.cloud/ips'; - const CLOUD_SERVER_DASH = 'https://my.quic.cloud'; - const CLOUD_SERVER_WP = 'https://wpapi.quic.cloud'; + const LOG_TAG = '❄️'; + + /** + * Base API server URL. + * + * @var string + */ + private $_cloud_server = 'https://api.quic.cloud'; + + /** + * Cloud IPs endpoint. + * + * @var string + */ + private $_cloud_ips = 'https://quic.cloud/ips'; + + /** + * Cloud dashboard URL. + * + * @var string + */ + private $_cloud_server_dash = 'https://my.quic.cloud'; + + /** + * Cloud WP API server URL. + * + * @var string + */ + private $_cloud_server_wp = 'https://wpapi.quic.cloud'; const SVC_D_ACTIVATE = 'd/activate'; const SVC_U_ACTIVATE = 'u/wp3/activate'; @@ -47,9 +76,9 @@ class Cloud extends Base { const IMGOPTM_TAKEN = 'img_optm-taken'; - const TTL_NODE = 3; // Days before node expired + const TTL_NODE = 3; // Days before node expired const EXPIRATION_REQ = 300; // Seconds of min interval between two unfinished requests - const TTL_IPS = 3; // Days for node ip list cache + const TTL_IPS = 3; // Days for node ip list cache const API_REPORT = 'wp/report'; const API_NEWS = 'news'; @@ -58,7 +87,12 @@ class Cloud extends Base { const API_REST_ECHO = 'tool/wp_rest_echo'; const API_SERVER_KEY_SIGN = 'key_sign'; - private static $CENTER_SVC_SET = array( + /** + * Center services hosted at the central API server. + * + * @var string[] + */ + private static $center_svc_set = [ self::SVC_D_ACTIVATE, self::SVC_U_ACTIVATE, self::SVC_D_ENABLE_CDN, @@ -76,24 +110,48 @@ class Cloud extends Base { // self::API_BETA_TEST, self::SVC_D_SETUP_TOKEN, self::SVC_D_DEL_CDN_DNS, - ); + ]; - private static $WP_SVC_SET = array( self::API_NEWS, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO ); + /** + * Services hosted on the WP API server. + * + * @var string[] + */ + private static $wp_svc_set = [ self::API_NEWS, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO ]; - // No api key needed for these services - private static $_PUB_SVC_SET = array( self::API_NEWS, self::API_REPORT, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO, self::SVC_D_V3UPGRADE, self::SVC_D_DASH ); + /** + * Public services that do not require an API key. + * + * @var string[] + */ + private static $_pub_svc_set = [ self::API_NEWS, self::API_REPORT, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO, self::SVC_D_V3UPGRADE, self::SVC_D_DASH ]; - private static $_QUEUE_SVC_SET = array( self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI ); + /** + * Services that should go through the queue. + * + * @var string[] + */ + private static $_queue_svc_set = [ self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI ]; - public static $SERVICES_LOAD_CHECK = array( + /** + * Services that need load check. + * + * @var string[] + */ + public static $services_load_check = [ // self::SVC_CCSS, // self::SVC_UCSS, // self::SVC_VPI, self::SVC_LQIP, self::SVC_HEALTH, - ); + ]; - public static $SERVICES = array( + /** + * All supported services. + * + * @var string[] + */ + public static $services = [ self::SVC_IMG_OPTM, self::SVC_PAGE_OPTM, self::SVC_CCSS, @@ -103,7 +161,7 @@ class Cloud extends Base { self::SVC_CDN, self::SVC_HEALTH, // self::SVC_QUEUE, - ); + ]; const TYPE_CLEAR_PROMO = 'clear_promo'; const TYPE_REDETECT_CLOUD = 'redetect_cloud'; @@ -116,14 +174,25 @@ class Cloud extends Base { const TYPE_RESET = 'reset'; const TYPE_SYNC_STATUS = 'sync_status'; + /** + * Summary data for cloud interactions. + * + * @var array<string,mixed> + */ protected $_summary; /** * Init * - * @since 3.0 + * @since 3.0 */ public function __construct() { + if ( defined( 'LITESPEED_DEV' ) && constant( 'LITESPEED_DEV' ) ) { + $this->_cloud_server = 'https://api.preview.quic.cloud'; + $this->_cloud_ips = 'https://api.preview.quic.cloud/ips'; + $this->_cloud_server_dash = 'https://my.preview.quic.cloud'; + $this->_cloud_server_wp = 'https://wpapi.quic.cloud'; + } $this->_summary = self::get_summary(); } @@ -133,10 +202,10 @@ public function __construct() { * @since 7.0 */ public function init_qc_prepare() { - if (empty($this->_summary['sk_b64'])) { + if ( empty( $this->_summary['sk_b64'] ) ) { $keypair = sodium_crypto_sign_keypair(); - $pk = base64_encode(sodium_crypto_sign_publickey($keypair)); - $sk = base64_encode(sodium_crypto_sign_secretkey($keypair)); + $pk = base64_encode( sodium_crypto_sign_publickey( $keypair ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode + $sk = base64_encode( sodium_crypto_sign_secretkey( $keypair ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode $this->_summary['pk_b64'] = $pk; $this->_summary['sk_b64'] = $sk; $this->save_summary(); @@ -158,66 +227,68 @@ public function init_qc() { $ref = $this->_get_ref_url(); // WPAPI REST echo dryrun - $req_data = array( - 'wp_pk_b64' => $this->_summary['pk_b64'], - ); - $echobox = self::post(self::API_REST_ECHO, $req_data); - if ($echobox === false) { - self::debugErr('REST Echo Failed!'); - $msg = __("QUIC.cloud's access to your WP REST API seems to be blocked.", 'litespeed-cache'); - Admin_Display::error($msg); - wp_redirect($ref); - return; + $echobox = self::post( self::API_REST_ECHO ); + if ( false === $echobox ) { + self::debugErr( 'REST Echo Failed!' ); + $msg = __( "QUIC.cloud's access to your WP REST API seems to be blocked.", 'litespeed-cache' ); + Admin_Display::error( $msg ); + wp_safe_redirect( $ref ); + exit; } - self::debug('echo succeeded'); + self::debug( 'echo succeeded' ); // Load separate thread echoed data from storage - if (empty($echobox['wpapi_ts']) || empty($echobox['wpapi_signature_b64'])) { - Admin_Display::error(__('Failed to get echo data from WPAPI', 'litespeed-cache')); - wp_redirect($ref); - return; + if ( empty( $echobox['wpapi_ts'] ) || empty( $echobox['wpapi_signature_b64'] ) ) { + Admin_Display::error( __( 'Failed to get echo data from WPAPI', 'litespeed-cache' ) ); + wp_safe_redirect( $ref ); + exit; } - $data = array( - 'wp_pk_b64' => $this->_summary['pk_b64'], - 'wpapi_ts' => $echobox['wpapi_ts'], + $data = [ + 'wp_pk_b64' => $this->_summary['pk_b64'], + 'wpapi_ts' => $echobox['wpapi_ts'], 'wpapi_signature_b64' => $echobox['wpapi_signature_b64'], - ); - $server_ip = $this->conf(self::O_SERVER_IP); - if ($server_ip) { + ]; + $server_ip = $this->conf( self::O_SERVER_IP ); + if ( $server_ip ) { $data['server_ip'] = $server_ip; } // Activation redirect - $param = array( + $param = [ 'site_url' => site_url(), - 'ver' => Core::VER, - 'data' => $data, - 'ref' => $ref, - ); - wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . urlencode(Utility::arr2str($param))); - exit(); + 'ver' => Core::VER, + 'data' => $data, + 'ref' => $ref, + ]; + wp_safe_redirect( $this->_cloud_server_dash . '/' . self::SVC_U_ACTIVATE . '?data=' . rawurlencode( Utility::arr2str( $param ) ) ); + exit; } /** * Decide the ref + * + * @param string|false $ref Ref slug. + * @return string */ private function _get_ref_url( $ref = false ) { $link = 'admin.php?page=litespeed'; - if ($ref == 'cdn') { + if ( 'cdn' === $ref ) { $link = 'admin.php?page=litespeed-cdn'; } - if ($ref == 'online') { + if ( 'online' === $ref ) { $link = 'admin.php?page=litespeed-general'; } - if (!empty($_GET['ref']) && $_GET['ref'] == 'cdn') { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $ref_get = ! empty( $_GET['ref'] ) ? sanitize_text_field( wp_unslash( $_GET['ref'] ) ) : ''; + if ( $ref_get && 'cdn' === $ref_get ) { $link = 'admin.php?page=litespeed-cdn'; } - if (!empty($_GET['ref']) && $_GET['ref'] == 'online') { + if ( $ref_get && 'online' === $ref_get ) { $link = 'admin.php?page=litespeed-general'; } - return get_admin_url(null, $link); + return get_admin_url( null, $link ); } /** @@ -228,47 +299,44 @@ private function _get_ref_url( $ref = false ) { public function init_qc_cli() { $this->init_qc_prepare(); - $server_ip = $this->conf(self::O_SERVER_IP); - if (!$server_ip) { - self::debugErr('Server IP needs to be set first!'); + $server_ip = $this->conf( self::O_SERVER_IP ); + if ( ! $server_ip ) { + self::debugErr( 'Server IP needs to be set first!' ); $msg = sprintf( - __('You need to set the %1$s first. Please use the command %2$s to set.', 'litespeed-cache'), - '`' . __('Server IP', 'litespeed-cache') . '`', + __( 'You need to set the %1$s first. Please use the command %2$s to set.', 'litespeed-cache' ), + '`' . __( 'Server IP', 'litespeed-cache' ) . '`', '`wp litespeed-option set server_ip __your_ip_value__`' ); - Admin_Display::error($msg); + Admin_Display::error( $msg ); return; } // WPAPI REST echo dryrun - $req_data = array( - 'wp_pk_b64' => $this->_summary['pk_b64'], - ); - $echobox = self::post(self::API_REST_ECHO, $req_data); - if ($echobox === false) { - self::debugErr('REST Echo Failed!'); - $msg = __("QUIC.cloud's access to your WP REST API seems to be blocked.", 'litespeed-cache'); - Admin_Display::error($msg); + $echobox = self::post( self::API_REST_ECHO ); + if ( false === $echobox ) { + self::debugErr( 'REST Echo Failed!' ); + $msg = __( "QUIC.cloud's access to your WP REST API seems to be blocked.", 'litespeed-cache' ); + Admin_Display::error( $msg ); return; } - self::debug('echo succeeded'); + self::debug( 'echo succeeded' ); // Load separate thread echoed data from storage - if (empty($echobox['wpapi_ts']) || empty($echobox['wpapi_signature_b64'])) { - self::debug('Resp: ', $echobox); - Admin_Display::error(__('Failed to get echo data from WPAPI', 'litespeed-cache')); + if ( empty( $echobox['wpapi_ts'] ) || empty( $echobox['wpapi_signature_b64'] ) ) { + self::debug( 'Resp: ', $echobox ); + Admin_Display::error( __( 'Failed to get echo data from WPAPI', 'litespeed-cache' ) ); return; } - $data = array( - 'wp_pk_b64' => $this->_summary['pk_b64'], - 'wpapi_ts' => $echobox['wpapi_ts'], + $data = [ + 'wp_pk_b64' => $this->_summary['pk_b64'], + 'wpapi_ts' => $echobox['wpapi_ts'], 'wpapi_signature_b64' => $echobox['wpapi_signature_b64'], - 'server_ip' => $server_ip, - ); + 'server_ip' => $server_ip, + ]; - $res = $this->post(self::SVC_D_ACTIVATE, $data); + $res = $this->post( self::SVC_D_ACTIVATE, $data ); return $res; } @@ -276,45 +344,50 @@ public function init_qc_cli() { * Init QC CDN setup (CLI) * * @since 7.0 + * + * @param string $method Method. + * @param string|bool $cert Cert path. + * @param string|bool $key Key path. + * @param string|bool $cf_token Cloudflare token. */ public function init_qc_cdn_cli( $method, $cert = false, $key = false, $cf_token = false ) { - if (!$this->activated()) { - Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + if ( ! $this->activated() ) { + Admin_Display::error( __( 'You need to activate QC first.', 'litespeed-cache' ) ); return; } - $server_ip = $this->conf(self::O_SERVER_IP); - if (!$server_ip) { - self::debugErr('Server IP needs to be set first!'); + $server_ip = $this->conf( self::O_SERVER_IP ); + if ( ! $server_ip ) { + self::debugErr( 'Server IP needs to be set first!' ); $msg = sprintf( - __('You need to set the %1$s first. Please use the command %2$s to set.', 'litespeed-cache'), - '`' . __('Server IP', 'litespeed-cache') . '`', + __( 'You need to set the %1$s first. Please use the command %2$s to set.', 'litespeed-cache' ), + '`' . __( 'Server IP', 'litespeed-cache' ) . '`', '`wp litespeed-option set server_ip __your_ip_value__`' ); - Admin_Display::error($msg); + Admin_Display::error( $msg ); return; } - if ($cert) { - if (!file_exists($cert) || !file_exists($key)) { - Admin_Display::error(__('Cert or key file does not exist.', 'litespeed-cache')); + if ( $cert ) { + if ( ! file_exists( $cert ) || ! file_exists( $key ) ) { + Admin_Display::error( __( 'Cert or key file does not exist.', 'litespeed-cache' ) ); return; } } - $data = array( - 'method' => $method, + $data = [ + 'method' => $method, 'server_ip' => $server_ip, - ); - if ($cert) { - $data['cert'] = File::read($cert); - $data['key'] = File::read($key); + ]; + if ( $cert ) { + $data['cert'] = File::read( $cert ); + $data['key'] = File::read( $key ); } - if ($cf_token) { + if ( $cf_token ) { $data['cf_token'] = $cf_token; } - $res = $this->post(self::SVC_D_ENABLE_CDN, $data); + $res = $this->post( self::SVC_D_ENABLE_CDN, $data ); return $res; } @@ -324,25 +397,25 @@ public function init_qc_cdn_cli( $method, $cert = false, $key = false, $cf_token * @since 7.0 */ public function link_qc() { - if (!$this->activated()) { - Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + if ( ! $this->activated() ) { + Admin_Display::error( __( 'You need to activate QC first.', 'litespeed-cache' ) ); return; } - $data = array( + $data = [ 'wp_ts' => time(), - ); - $data['wp_signature_b64'] = $this->_sign_b64($data['wp_ts']); + ]; + $data['wp_signature_b64'] = $this->_sign_b64( $data['wp_ts'] ); // Activation redirect - $param = array( + $param = [ 'site_url' => site_url(), - 'ver' => Core::VER, - 'data' => $data, - 'ref' => $this->_get_ref_url(), - ); - wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_LINK . '?data=' . urlencode(Utility::arr2str($param))); - exit(); + 'ver' => Core::VER, + 'data' => $data, + 'ref' => $this->_get_ref_url(), + ]; + wp_safe_redirect( $this->_cloud_server_dash . '/' . self::SVC_U_LINK . '?data=' . rawurlencode( Utility::arr2str( $param ) ) ); + exit; } /** @@ -351,13 +424,13 @@ public function link_qc() { * @since 7.0 */ public function cdn_status_cli() { - if (!$this->activated()) { - Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + if ( ! $this->activated() ) { + Admin_Display::error( __( 'You need to activate QC first.', 'litespeed-cache' ) ); return; } - $data = array(); - $res = $this->post(self::SVC_D_STATUS_CDN_CLI, $data); + $data = []; + $res = $this->post( self::SVC_D_STATUS_CDN_CLI, $data ); return $res; } @@ -365,18 +438,21 @@ public function cdn_status_cli() { * Link to QC Account for CLI * * @since 7.0 + * + * @param string $email Account email. + * @param string $key API key. */ public function link_qc_cli( $email, $key ) { - if (!$this->activated()) { - Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + if ( ! $this->activated() ) { + Admin_Display::error( __( 'You need to activate QC first.', 'litespeed-cache' ) ); return; } - $data = array( + $data = [ 'qc_acct_email' => $email, - 'qc_acct_apikey' => $key, - ); - $res = $this->post(self::SVC_D_LINK, $data); + 'qc_acct_apikey'=> $key, + ]; + $res = $this->post( self::SVC_D_LINK, $data ); return $res; } @@ -384,18 +460,20 @@ public function link_qc_cli( $email, $key ) { * API link parsed call to QC * * @since 7.0 + * + * @param string $action2 Action slug. */ public function api_link_call( $action2 ) { - if (!$this->activated()) { - Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + if ( ! $this->activated() ) { + Admin_Display::error( __( 'You need to activate QC first.', 'litespeed-cache' ) ); return; } - $data = array( + $data = [ 'action2' => $action2, - ); - $res = $this->post(self::SVC_D_API, $data); - self::debug('API link call result: ', $res); + ]; + $res = $this->post( self::SVC_D_API, $data ); + self::debug( 'API link call result: ', $res ); } /** @@ -404,84 +482,90 @@ public function api_link_call( $action2 ) { * @since 7.0 */ public function enable_cdn() { - if (!$this->activated()) { - Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + if ( ! $this->activated() ) { + Admin_Display::error( __( 'You need to activate QC first.', 'litespeed-cache' ) ); return; } - $data = array( + $data = [ 'wp_ts' => time(), - ); - $data['wp_signature_b64'] = $this->_sign_b64($data['wp_ts']); + ]; + $data['wp_signature_b64'] = $this->_sign_b64( $data['wp_ts'] ); // Activation redirect - $param = array( + $param = [ 'site_url' => site_url(), - 'ver' => Core::VER, - 'data' => $data, - 'ref' => $this->_get_ref_url(), - ); - wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ENABLE_CDN . '?data=' . urlencode(Utility::arr2str($param))); - exit(); + 'ver' => Core::VER, + 'data' => $data, + 'ref' => $this->_get_ref_url(), + ]; + wp_safe_redirect( $this->_cloud_server_dash . '/' . self::SVC_U_ENABLE_CDN . '?data=' . rawurlencode( Utility::arr2str( $param ) ) ); + exit; } /** * Encrypt data for cloud req * * @since 7.0 + * + * @param string|int $data Data to sign. + * @return string|false */ private function _sign_b64( $data ) { - if (empty($this->_summary['sk_b64'])) { - self::debugErr('No sk to sign.'); + if ( empty( $this->_summary['sk_b64'] ) ) { + self::debugErr( 'No sk to sign.' ); return false; } - $sk = base64_decode($this->_summary['sk_b64']); - if (strlen($sk) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) { - self::debugErr('Invalid local sign sk length.'); + $sk = base64_decode( $this->_summary['sk_b64'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + if ( strlen( $sk ) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES ) { + self::debugErr( 'Invalid local sign sk length.' ); // Reset local pk/sk - unset($this->_summary['pk_b64']); - unset($this->_summary['sk_b64']); + unset( $this->_summary['pk_b64'] ); + unset( $this->_summary['sk_b64'] ); $this->save_summary(); - self::debug('Clear local sign pk/sk pair.'); + self::debug( 'Clear local sign pk/sk pair.' ); return false; } - $signature = sodium_crypto_sign_detached((string) $data, $sk); - return base64_encode($signature); + $signature = sodium_crypto_sign_detached( (string) $data, $sk ); + return base64_encode( $signature ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode } /** * Load server pk from cloud * * @since 7.0 + * + * @param bool $from_wpapi Load from WP API server. + * @return string|false Binary public key or false. */ private function _load_server_pk( $from_wpapi = false ) { // Load cloud pk - $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY_SIGN; - if ($from_wpapi) { - $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY_SIGN; + $server_key_url = $this->_cloud_server . '/' . self::API_SERVER_KEY_SIGN; + if ( $from_wpapi ) { + $server_key_url = $this->_cloud_server_wp . '/' . self::API_SERVER_KEY_SIGN; } - $resp = wp_safe_remote_get($server_key_url); - if (is_wp_error($resp)) { - self::debugErr('Failed to load key: ' . $resp->get_error_message()); + $resp = wp_safe_remote_get( $server_key_url ); + if ( is_wp_error( $resp ) ) { + self::debugErr( 'Failed to load key: ' . $resp->get_error_message() ); return false; } - $pk = trim($resp['body']); - self::debug('Loaded key from ' . $server_key_url . ': ' . $pk); - $cloud_pk = base64_decode($pk); - if (strlen($cloud_pk) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) { - self::debugErr('Invalid cloud public key length.'); + $pk = trim( $resp['body'] ); + self::debug( 'Loaded key from ' . $server_key_url . ': ' . $pk ); + $cloud_pk = base64_decode( $pk ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + if ( strlen( $cloud_pk ) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES ) { + self::debugErr( 'Invalid cloud public key length.' ); return false; } - $sk = base64_decode($this->_summary['sk_b64']); - if (strlen($sk) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) { - self::debugErr('Invalid local secret key length.'); + $sk = base64_decode( $this->_summary['sk_b64'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + if ( strlen( $sk ) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES ) { + self::debugErr( 'Invalid local secret key length.' ); // Reset local pk/sk - unset($this->_summary['pk_b64']); - unset($this->_summary['sk_b64']); + unset( $this->_summary['pk_b64'] ); + unset( $this->_summary['sk_b64'] ); $this->save_summary(); - self::debug('Unset local pk/sk pair.'); + self::debug( 'Unset local pk/sk pair.' ); return false; } @@ -495,47 +579,58 @@ private function _load_server_pk( $from_wpapi = false ) { * @since 7.0 */ public function wp_rest_echo() { - self::debug('Parsing echo', $_POST); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + self::debug( 'Parsing echo', $_POST ); + + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $ts = !empty( $_POST['wpapi_ts'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['wpapi_ts'] ) ) : 0; + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $sig = !empty( $_POST['wpapi_signature_b64'] ) ? sanitize_text_field( wp_unslash( $_POST['wpapi_signature_b64'] ) ) : ''; - if (empty($_POST['wpapi_ts']) || empty($_POST['wpapi_signature_b64'])) { - return self::err('No echo data'); + if ( empty( $ts ) || empty( $sig ) ) { + return self::err( 'No echo data' ); } - $is_valid = $this->_validate_signature($_POST['wpapi_signature_b64'], $_POST['wpapi_ts'], true); - if (!$is_valid) { - return self::err('Data validation from WPAPI REST Echo failed'); + $is_valid = $this->_validate_signature( $sig, $ts, true ); + if ( ! $is_valid ) { + return self::err( 'Data validation from WPAPI REST Echo failed' ); } - $diff = time() - $_POST['wpapi_ts']; - if (abs($diff) > 86400) { - self::debugErr('WPAPI echo data timeout [diff] ' . $diff); - return self::err('Echo data expired'); + $diff = time() - $ts; + if ( abs( $diff ) > 86400 ) { + self::debugErr( 'WPAPI echo data timeout [diff] ' . $diff ); + return self::err( 'Echo data expired' ); } - $signature_b64 = $this->_sign_b64($_POST['wpapi_ts']); - self::debug('Response to echo [signature_b64] ' . $signature_b64); - return self::ok(array( 'signature_b64' => $signature_b64 )); + $signature_b64 = $this->_sign_b64( $ts ); + self::debug( 'Response to echo [signature_b64] ' . $signature_b64 ); + return self::ok( [ 'signature_b64' => $signature_b64 ] ); } /** * Validate cloud data * * @since 7.0 + * + * @param string $signature_b64 Base64 signature. + * @param string $data Data to validate. + * @param bool $from_wpapi Whether the signature is from WP API server. + * @return bool */ private function _validate_signature( $signature_b64, $data, $from_wpapi = false ) { // Try validation try { - $cloud_pk = $this->_load_server_pk($from_wpapi); - if (!$cloud_pk) { + $cloud_pk = $this->_load_server_pk( $from_wpapi ); + if ( ! $cloud_pk ) { return false; } - $signature = base64_decode($signature_b64); - $is_valid = sodium_crypto_sign_verify_detached($signature, $data, $cloud_pk); - } catch (\SodiumException $e) { - self::debugErr('Decryption failed: ' . $e->getMessage()); + $signature = base64_decode( $signature_b64 ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + $is_valid = sodium_crypto_sign_verify_detached( $signature, $data, $cloud_pk ); + } catch ( \SodiumException $e ) { + self::debugErr( 'Decryption failed: ' . esc_html( $e->getMessage() ) ); return false; } - self::debug('Signature validation result: ' . ($is_valid ? 'true' : 'false')); + self::debug( 'Signature validation result: ' . ( $is_valid ? 'true' : 'false' ) ); return $is_valid; } @@ -543,68 +638,83 @@ private function _validate_signature( $signature_b64, $data, $from_wpapi = false * Finish qc activation after redirection back from QC * * @since 7.0 + * + * @param string|false $ref Ref slug. */ public function finish_qc_activation( $ref = false ) { - if (empty($_GET['qc_activated']) || empty($_GET['qc_ts']) || empty($_GET['qc_signature_b64'])) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended + $qc_activated = !empty( $_GET['qc_activated'] ) ? sanitize_text_field( wp_unslash( $_GET['qc_activated'] ) ) : ''; + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended + $qc_ts = !empty( $_GET['qc_ts'] ) ? sanitize_text_field( wp_unslash( $_GET['qc_ts'] ) ) : ''; + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended + $qc_sig = !empty( $_GET['qc_signature_b64'] ) ? sanitize_text_field( wp_unslash( $_GET['qc_signature_b64'] ) ) : ''; + + if ( ! $qc_activated || ! $qc_ts || ! $qc_sig ) { return; } - $data_to_validate_signature = array( + $data_to_validate_signature = [ 'wp_pk_b64' => $this->_summary['pk_b64'], - 'qc_ts' => $_GET['qc_ts'], - ); - $is_valid = $this->_validate_signature($_GET['qc_signature_b64'], implode('', $data_to_validate_signature)); - if (!$is_valid) { - self::debugErr('Failed to validate qc activation data'); - Admin_Display::error(sprintf(__('Failed to validate %s activation data.', 'litespeed-cache'), 'QUIC.cloud')); + 'qc_ts' => $qc_ts, + ]; + $is_valid = $this->_validate_signature( $qc_sig, implode( '', $data_to_validate_signature ) ); + if ( ! $is_valid ) { + self::debugErr( 'Failed to validate qc activation data' ); + Admin_Display::error( sprintf( __( 'Failed to validate %s activation data.', 'litespeed-cache' ), 'QUIC.cloud' ) ); return; } - self::debug('QC activation status: ' . $_GET['qc_activated']); - if (!in_array($_GET['qc_activated'], array( 'anonymous', 'linked', 'cdn' ))) { - self::debugErr('Failed to parse qc activation status'); - Admin_Display::error(sprintf(__('Failed to parse %s activation status.', 'litespeed-cache'), 'QUIC.cloud')); + self::debug( 'QC activation status: ' . $qc_activated ); + if ( ! in_array( $qc_activated, [ 'anonymous', 'linked', 'cdn' ], true ) ) { + self::debugErr( 'Failed to parse qc activation status' ); + Admin_Display::error( sprintf( __( 'Failed to parse %s activation status.', 'litespeed-cache' ), 'QUIC.cloud' ) ); return; } - $diff = time() - $_GET['qc_ts']; - if (abs($diff) > 86400) { - self::debugErr('QC activation data timeout [diff] ' . $diff); - Admin_Display::error(sprintf(__('%s activation data expired.', 'litespeed-cache'), 'QUIC.cloud')); + $diff = time() - (int) $qc_ts; + if ( abs( $diff ) > 86400 ) { + self::debugErr( 'QC activation data timeout [diff] ' . $diff ); + Admin_Display::error( sprintf( __( '%s activation data expired.', 'litespeed-cache' ), 'QUIC.cloud' ) ); return; } - $main_domain = !empty($_GET['main_domain']) ? $_GET['main_domain'] : false; - $this->update_qc_activation($_GET['qc_activated'], $main_domain); + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended + $main_domain = ! empty( $_GET['main_domain'] ) ? sanitize_text_field( wp_unslash( $_GET['main_domain'] ) ) : false; + $this->update_qc_activation( $qc_activated, $main_domain ); - wp_redirect($this->_get_ref_url($ref)); + wp_safe_redirect( $this->_get_ref_url( $ref ) ); + exit; } /** * Finish qc activation process * * @since 7.0 + * + * @param string $qc_activated Activation status. + * @param string|bool $main_domain Main domain. + * @param bool $quite Quiet flag. */ public function update_qc_activation( $qc_activated, $main_domain = false, $quite = false ) { $this->_summary['qc_activated'] = $qc_activated; - if ($main_domain) { + if ( $main_domain ) { $this->_summary['main_domain'] = $main_domain; } $this->save_summary(); - $msg = sprintf(__('Congratulations, %s successfully set this domain up for the anonymous online services.', 'litespeed-cache'), 'QUIC.cloud'); - if ($qc_activated == 'linked') { - $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services.', 'litespeed-cache'), 'QUIC.cloud'); + $msg = sprintf( __( 'Congratulations, %s successfully set this domain up for the anonymous online services.', 'litespeed-cache' ), 'QUIC.cloud' ); + if ( 'linked' === $qc_activated ) { + $msg = sprintf( __( 'Congratulations, %s successfully set this domain up for the online services.', 'litespeed-cache' ), 'QUIC.cloud' ); // Sync possible partner info $this->sync_usage(); } - if ($qc_activated == 'cdn') { - $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache'), 'QUIC.cloud'); + if ( 'cdn' === $qc_activated ) { + $msg = sprintf( __( 'Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache' ), 'QUIC.cloud' ); // Turn on CDN option - $this->cls('Conf')->update_confs(array( self::O_CDN_QUIC => true )); + $this->cls( 'Conf' )->update_confs( [ self::O_CDN_QUIC => true ] ); } - if (!$quite) { - Admin_Display::success('🎊 ' . $msg); + if ( ! $quite ) { + Admin_Display::success( '🎊 ' . $msg ); } $this->_clear_reset_qc_reg_msg(); @@ -613,36 +723,48 @@ public function update_qc_activation( $qc_activated, $main_domain = false, $quit } /** - * Load QC status for dash usage + * Load QC status for dash usage. * Format to translate: `<a href="{#xxx#}" class="button button-primary">xxxx</a><a href="{#xxx#}">xxxx2</a>` * * @since 7.0 + * + * @param string $type Type. + * @param bool $force Force refresh. + * @return string */ public function load_qc_status_for_dash( $type, $force = false ) { - return Str::translate_qc_apis($this->_load_qc_status_for_dash($type, $force)); + return Str::translate_qc_apis( $this->_load_qc_status_for_dash( $type, $force ) ); } + + /** + * Internal: load QC status HTML for dash. + * + * @param string $type Type. + * @param bool $force Force refresh. + * @return string + */ private function _load_qc_status_for_dash( $type, $force = false ) { if ( - !$force && - !empty($this->_summary['mini_html']) && - isset($this->_summary['mini_html'][$type]) && - !empty($this->_summary['mini_html']['ttl.' . $type]) && - $this->_summary['mini_html']['ttl.' . $type] > time() + ! $force && + ! empty( $this->_summary['mini_html'] ) && + isset( $this->_summary['mini_html'][ $type ] ) && + ! empty( $this->_summary['mini_html'][ 'ttl.' . $type ] ) && + $this->_summary['mini_html'][ 'ttl.' . $type ] > time() ) { - return Str::safe_html($this->_summary['mini_html'][$type]); + return Str::safe_html( $this->_summary['mini_html'][ $type ] ); } // Try to update dash content - $data = self::post(self::SVC_D_DASH, array( 'action2' => $type == 'cdn_dash_mini' ? 'cdn_dash' : $type )); - if (!empty($data['qc_activated'])) { + $data = self::post( self::SVC_D_DASH, [ 'action2' => ( 'cdn_dash_mini' === $type ? 'cdn_dash' : $type ) ] ); + if ( ! empty( $data['qc_activated'] ) ) { // Sync conf as changed - if (empty($this->_summary['qc_activated']) || $this->_summary['qc_activated'] != $data['qc_activated']) { - $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache'), 'QUIC.cloud'); - Admin_Display::success('🎊 ' . $msg); + if ( empty( $this->_summary['qc_activated'] ) || $this->_summary['qc_activated'] !== $data['qc_activated'] ) { + $msg = sprintf( __( 'Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache' ), 'QUIC.cloud' ); + Admin_Display::success( '🎊 ' . $msg ); $this->_clear_reset_qc_reg_msg(); // Turn on CDN option - $this->cls('Conf')->update_confs(array( self::O_CDN_QUIC => true )); - $this->cls('CDN\Quic')->try_sync_conf(true); + $this->cls( 'Conf' )->update_confs( [ self::O_CDN_QUIC => true ] ); + $this->cls( 'CDN\Quic' )->try_sync_conf( true ); } $this->_summary['qc_activated'] = $data['qc_activated']; @@ -650,8 +772,8 @@ private function _load_qc_status_for_dash( $type, $force = false ) { } // Show the info - if (isset($this->_summary['mini_html'][$type])) { - return Str::safe_html($this->_summary['mini_html'][$type]); + if ( isset( $this->_summary['mini_html'][ $type ] ) ) { + return Str::safe_html( $this->_summary['mini_html'][ $type ] ); } return ''; @@ -663,29 +785,32 @@ private function _load_qc_status_for_dash( $type, $force = false ) { * @since 7.0 */ public function update_cdn_status() { - if (empty($_POST['qc_activated']) || !in_array($_POST['qc_activated'], array( 'anonymous', 'linked', 'cdn', 'deleted' ))) { - return self::err('lack_of_params'); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $qc_activated = !empty( $_POST['qc_activated'] ) ? sanitize_text_field( wp_unslash( $_POST['qc_activated'] ) ) : ''; + + if ( !$qc_activated || ! in_array( $qc_activated, [ 'anonymous', 'linked', 'cdn', 'deleted' ], true ) ) { + return self::err( 'lack_of_params' ); } - self::debug('update_cdn_status request hash: ' . $_POST['qc_activated']); + self::debug( 'update_cdn_status request hash: ' . $qc_activated ); - if ($_POST['qc_activated'] == 'deleted') { + if ( 'deleted' === $qc_activated ) { $this->_reset_qc_reg(); } else { - $this->_summary['qc_activated'] = $_POST['qc_activated']; + $this->_summary['qc_activated'] = $qc_activated; $this->save_summary(); } - if ($_POST['qc_activated'] == 'cdn') { - $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache'), 'QUIC.cloud'); - Admin_Display::success('🎊 ' . $msg); + if ( 'cdn' === $qc_activated ) { + $msg = sprintf( __( 'Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache' ), 'QUIC.cloud' ); + Admin_Display::success( '🎊 ' . $msg ); $this->_clear_reset_qc_reg_msg(); // Turn on CDN option - $this->cls('Conf')->update_confs(array( self::O_CDN_QUIC => true )); - $this->cls('CDN\Quic')->try_sync_conf(true); + $this->cls( 'Conf' )->update_confs( [ self::O_CDN_QUIC => true ] ); + $this->cls( 'CDN\Quic' )->try_sync_conf( true ); } - return self::ok(array( 'qc_activated' => $_POST['qc_activated'] )); + return self::ok( [ 'qc_activated' => $qc_activated ] ); } /** @@ -694,20 +819,20 @@ public function update_cdn_status() { * @since 7.0 */ public function reset_qc() { - unset($this->_summary['pk_b64']); - unset($this->_summary['sk_b64']); - unset($this->_summary['qc_activated']); - if (!empty($this->_summary['partner'])) { - unset($this->_summary['partner']); + unset( $this->_summary['pk_b64'] ); + unset( $this->_summary['sk_b64'] ); + unset( $this->_summary['qc_activated'] ); + if ( ! empty( $this->_summary['partner'] ) ) { + unset( $this->_summary['partner'] ); } $this->save_summary(); - self::debug('Clear local QC activation.'); + self::debug( 'Clear local QC activation.' ); $this->clear_cloud(); - Admin_Display::success(sprintf(__('Reset %s activation successfully.', 'litespeed-cache'), 'QUIC.cloud')); - wp_redirect($this->_get_ref_url()); - exit(); + Admin_Display::success( sprintf( __( 'Reset %s activation successfully.', 'litespeed-cache' ), 'QUIC.cloud' ) ); + wp_safe_redirect( $this->_get_ref_url() ); + exit; } /** @@ -716,26 +841,26 @@ public function reset_qc() { * @since 3.0 */ public function check_dev_version() { - if (!preg_match('/[^\d\.]/', Core::VER)) { + if ( ! preg_match( '/[^\d\.]/', Core::VER ) ) { return; } - $last_check = empty($this->_summary['last_request.' . self::API_VER]) ? 0 : $this->_summary['last_request.' . self::API_VER]; + $last_check = empty( $this->_summary[ 'last_request.' . self::API_VER ] ) ? 0 : $this->_summary[ 'last_request.' . self::API_VER ]; - if (time() - $last_check > 86400) { - $auto_v = self::version_check('dev'); - if (!empty($auto_v['dev'])) { - self::save_summary(array( 'version.dev' => $auto_v['dev'] )); + if ( time() - $last_check > 86400 ) { + $auto_v = self::version_check( 'dev' ); + if ( ! empty( $auto_v['dev'] ) ) { + self::save_summary( [ 'version.dev' => $auto_v['dev'] ] ); } } - if (empty($this->_summary['version.dev'])) { + if ( empty( $this->_summary['version.dev'] ) ) { return; } - self::debug('Latest dev version ' . $this->_summary['version.dev']); + self::debug( 'Latest dev version ' . $this->_summary['version.dev'] ); - if (version_compare($this->_summary['version.dev'], Core::VER, '<=')) { + if ( version_compare( $this->_summary['version.dev'], Core::VER, '<=' ) ) { return; } @@ -746,19 +871,27 @@ public function check_dev_version() { /** * Check latest version * - * @since 2.9 + * @since 2.9 * @access public + * + * @param string|false $src Source. + * @return mixed */ public static function version_check( $src = false ) { - $req_data = array( - 'v' => defined('LSCWP_CUR_V') ? LSCWP_CUR_V : '', + $req_data = [ + 'v' => defined( 'LSCWP_CUR_V' ) ? LSCWP_CUR_V : '', 'src' => $src, 'php' => phpversion(), - ); - if (defined('LITESPEED_ERR')) { - $req_data['err'] = base64_encode(!is_string(LITESPEED_ERR) ? \json_encode(LITESPEED_ERR) : LITESPEED_ERR); + ]; + // If code ver is smaller than db ver, bypass + if ( ! empty( $req_data['v'] ) && version_compare( Core::VER, $req_data['v'], '<' ) ) { + return; } - $data = self::post(self::API_VER, $req_data); + if ( defined( 'LITESPEED_ERR' ) ) { + $litespeed_err = constant( 'LITESPEED_ERR' ); + $req_data['err'] = base64_encode( ! is_string( $litespeed_err ) ? wp_json_encode( $litespeed_err ) : $litespeed_err ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode + } + $data = self::post( self::API_VER, $req_data ); return $data; } @@ -771,11 +904,11 @@ public static function version_check( $src = false ) { public function news() { $this->_update_news(); - if (empty($this->_summary['news.new'])) { + if ( empty( $this->_summary['news.new'] ) ) { return; } - if (!empty($this->_summary['news.plugin']) && Activation::cls()->dash_notifier_is_plugin_active($this->_summary['news.plugin'])) { + if ( ! empty( $this->_summary['news.plugin'] ) && Activation::cls()->dash_notifier_is_plugin_active( $this->_summary['news.plugin'] ) ) { return; } @@ -788,32 +921,32 @@ public function news() { * @since 2.9.9.1 */ private function _update_news() { - if (!empty($this->_summary['news.utime']) && time() - $this->_summary['news.utime'] < 86400 * 7) { + if ( ! empty( $this->_summary['news.utime'] ) && time() - $this->_summary['news.utime'] < 86400 * 7 ) { return; } - self::save_summary(array( 'news.utime' => time() )); + self::save_summary( [ 'news.utime' => time() ] ); - $data = self::get(self::API_NEWS); - if (empty($data['id'])) { + $data = self::get( self::API_NEWS ); + if ( empty( $data['id'] ) ) { return; } // Save news - if (!empty($this->_summary['news.id']) && $this->_summary['news.id'] == $data['id']) { + if ( ! empty( $this->_summary['news.id'] ) && (string) $this->_summary['news.id'] === (string) $data['id'] ) { return; } $this->_summary['news.id'] = $data['id']; - $this->_summary['news.plugin'] = !empty($data['plugin']) ? $data['plugin'] : ''; - $this->_summary['news.title'] = !empty($data['title']) ? $data['title'] : ''; - $this->_summary['news.content'] = !empty($data['content']) ? $data['content'] : ''; - $this->_summary['news.zip'] = !empty($data['zip']) ? $data['zip'] : ''; + $this->_summary['news.plugin'] = ! empty( $data['plugin'] ) ? $data['plugin'] : ''; + $this->_summary['news.title'] = ! empty( $data['title'] ) ? $data['title'] : ''; + $this->_summary['news.content'] = ! empty( $data['content'] ) ? $data['content'] : ''; + $this->_summary['news.zip'] = ! empty( $data['zip'] ) ? $data['zip'] : ''; $this->_summary['news.new'] = 1; - if ($this->_summary['news.plugin']) { - $plugin_info = Activation::cls()->dash_notifier_get_plugin_info($this->_summary['news.plugin']); - if ($plugin_info && !empty($plugin_info->name)) { + if ( $this->_summary['news.plugin'] ) { + $plugin_info = Activation::cls()->dash_notifier_get_plugin_info( $this->_summary['news.plugin'] ); + if ( $plugin_info && ! empty( $plugin_info->name ) ) { $this->_summary['news.plugin_name'] = $plugin_info->name; } } @@ -824,10 +957,14 @@ private function _update_news() { /** * Check if contains a package in a service or not * - * @since 4.0 + * @since 4.0 + * + * @param string $service Service. + * @param int $pkg Package flag. + * @return bool */ public function has_pkg( $service, $pkg ) { - if (!empty($this->_summary['usage.' . $service]['pkgs']) && $this->_summary['usage.' . $service]['pkgs'] & $pkg) { + if ( ! empty( $this->_summary[ 'usage.' . $service ]['pkgs'] ) && ( $this->_summary[ 'usage.' . $service ]['pkgs'] & $pkg ) ) { return true; } @@ -837,28 +974,32 @@ public function has_pkg( $service, $pkg ) { /** * Get allowance of current service * - * @since 3.0 + * @since 3.0 * @access private + * + * @param string $service Service. + * @param string|bool $err Error code by ref. + * @return int */ public function allowance( $service, &$err = false ) { // Only auto sync usage at most one time per day - if (empty($this->_summary['last_request.' . self::SVC_D_USAGE]) || time() - $this->_summary['last_request.' . self::SVC_D_USAGE] > 86400) { + if ( empty( $this->_summary[ 'last_request.' . self::SVC_D_USAGE ] ) || time() - $this->_summary[ 'last_request.' . self::SVC_D_USAGE ] > 86400 ) { $this->sync_usage(); } - if (in_array($service, array( self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI ))) { + if ( in_array( $service, [ self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI ], true ) ) { // @since 4.2 $service = self::SVC_PAGE_OPTM; } - if (empty($this->_summary['usage.' . $service])) { + if ( empty( $this->_summary[ 'usage.' . $service ] ) ) { return 0; } - $usage = $this->_summary['usage.' . $service]; + $usage = $this->_summary[ 'usage.' . $service ]; // Image optm is always free $allowance_max = 0; - if ($service == self::SVC_IMG_OPTM) { + if ( self::SVC_IMG_OPTM === $service ) { $allowance_max = self::IMG_OPTM_DEFAULT_GROUP; } @@ -866,15 +1007,15 @@ public function allowance( $service, &$err = false ) { $err = 'out_of_quota'; - if ($allowance > 0) { - if ($allowance_max && $allowance_max < $allowance) { + if ( $allowance > 0 ) { + if ( $allowance_max && $allowance_max < $allowance ) { $allowance = $allowance_max; } // Daily limit @since 4.2 - if (isset($usage['remaining_daily_quota']) && $usage['remaining_daily_quota'] >= 0 && $usage['remaining_daily_quota'] < $allowance) { + if ( isset( $usage['remaining_daily_quota'] ) && $usage['remaining_daily_quota'] >= 0 && $usage['remaining_daily_quota'] < $allowance ) { $allowance = $usage['remaining_daily_quota']; - if (!$allowance) { + if ( ! $allowance ) { $err = 'out_of_daily_quota'; } } @@ -883,33 +1024,33 @@ public function allowance( $service, &$err = false ) { } // Check Pay As You Go balance - if (empty($usage['pag_bal'])) { + if ( empty( $usage['pag_bal'] ) ) { return $allowance_max; } - if ($allowance_max && $allowance_max < $usage['pag_bal']) { + if ( $allowance_max && $allowance_max < $usage['pag_bal'] ) { return $allowance_max; } - return $usage['pag_bal']; + return (int) $usage['pag_bal']; } /** * Sync Cloud usage summary data * - * @since 3.0 + * @since 3.0 * @access public */ public function sync_usage() { - $usage = $this->_post(self::SVC_D_USAGE); - if (!$usage) { + $usage = $this->_post( self::SVC_D_USAGE ); + if ( ! $usage ) { return; } - self::debug('sync_usage ' . \json_encode($usage)); + self::debug( 'sync_usage ' . wp_json_encode( $usage ) ); - foreach (self::$SERVICES as $v) { - $this->_summary['usage.' . $v] = !empty($usage[$v]) ? $usage[$v] : false; + foreach ( self::$services as $v ) { + $this->_summary[ 'usage.' . $v ] = ! empty( $usage[ $v ] ) ? $usage[ $v ] : false; } self::save_summary(); @@ -920,71 +1061,75 @@ public function sync_usage() { /** * Clear all existing cloud nodes for future reconnect * - * @since 3.0 + * @since 3.0 * @access public */ public function clear_cloud() { - foreach (self::$SERVICES as $service) { - if (isset($this->_summary['server.' . $service])) { - unset($this->_summary['server.' . $service]); + foreach ( self::$services as $service ) { + if ( isset( $this->_summary[ 'server.' . $service ] ) ) { + unset( $this->_summary[ 'server.' . $service ] ); } - if (isset($this->_summary['server_date.' . $service])) { - unset($this->_summary['server_date.' . $service]); + if ( isset( $this->_summary[ 'server_date.' . $service ] ) ) { + unset( $this->_summary[ 'server_date.' . $service ] ); } } self::save_summary(); - self::debug('Cleared all local service node caches'); + self::debug( 'Cleared all local service node caches' ); } /** - * ping clouds to find the fastest node + * Ping clouds to find the fastest node * - * @since 3.0 + * @since 3.0 * @access public + * + * @param string $service Service. + * @param bool $force Force redetect. + * @return string|false */ public function detect_cloud( $service, $force = false ) { - if (in_array($service, self::$CENTER_SVC_SET)) { - return self::CLOUD_SERVER; + if ( in_array( $service, self::$center_svc_set, true ) ) { + return $this->_cloud_server; } - if (in_array($service, self::$WP_SVC_SET)) { - return self::CLOUD_SERVER_WP; + if ( in_array( $service, self::$wp_svc_set, true ) ) { + return $this->_cloud_server_wp; } // Check if the stored server needs to be refreshed - if (!$force) { + if ( ! $force ) { if ( - !empty($this->_summary['server.' . $service]) && - !empty($this->_summary['server_date.' . $service]) && - $this->_summary['server_date.' . $service] > time() - 86400 * self::TTL_NODE + ! empty( $this->_summary[ 'server.' . $service ] ) && + ! empty( $this->_summary[ 'server_date.' . $service ] ) && + $this->_summary[ 'server_date.' . $service ] > time() - 86400 * self::TTL_NODE ) { - $server = $this->_summary['server.' . $service]; - if (!strpos(self::CLOUD_SERVER, 'preview.') && !strpos($server, 'preview.')) { + $server = $this->_summary[ 'server.' . $service ]; + if ( false === strpos( $this->_cloud_server, 'preview.' ) && false === strpos( $server, 'preview.' ) ) { return $server; } - if (strpos(self::CLOUD_SERVER, 'preview.') && strpos($server, 'preview.')) { + if ( false !== strpos( $this->_cloud_server, 'preview.' ) && false !== strpos( $server, 'preview.' ) ) { return $server; } } } - if (!$service || !in_array($service, self::$SERVICES)) { - $msg = __('Cloud Error', 'litespeed-cache') . ': ' . $service; - Admin_Display::error($msg); + if ( ! $service || ! in_array( $service, self::$services, true ) ) { + $msg = __( 'Cloud Error', 'litespeed-cache' ) . ': ' . $service; + Admin_Display::error( $msg ); return false; } // Send request to Quic Online Service - $json = $this->_post(self::SVC_D_NODES, array( 'svc' => $this->_maybe_queue($service) )); + $json = $this->_post( self::SVC_D_NODES, [ 'svc' => $this->_maybe_queue( $service ) ] ); // Check if get list correctly - if (empty($json['list']) || !is_array($json['list'])) { - self::debug('request cloud list failed: ', $json); + if ( empty( $json['list'] ) || ! is_array( $json['list'] ) ) { + self::debug( 'request cloud list failed: ', $json ); - if ($json) { - $msg = __('Cloud Error', 'litespeed-cache') . ": [Service] $service [Info] " . \json_encode($json); - Admin_Display::error($msg); + if ( $json ) { + $msg = __( 'Cloud Error', 'litespeed-cache' ) . ": [Service] $service [Info] " . wp_json_encode( $json ); + Admin_Display::error( $msg ); } return false; @@ -992,115 +1137,120 @@ public function detect_cloud( $service, $force = false ) { // Ping closest cloud $valid_clouds = false; - if (!empty($json['list_preferred'])) { - $valid_clouds = $this->_get_closest_nodes($json['list_preferred'], $service); + if ( ! empty( $json['list_preferred'] ) ) { + $valid_clouds = $this->_get_closest_nodes( $json['list_preferred'], $service ); } - if (!$valid_clouds) { - $valid_clouds = $this->_get_closest_nodes($json['list'], $service); + if ( ! $valid_clouds ) { + $valid_clouds = $this->_get_closest_nodes( $json['list'], $service ); } - if (!$valid_clouds) { + if ( ! $valid_clouds ) { return false; } // Check server load - if (in_array($service, self::$SERVICES_LOAD_CHECK)) { + if ( in_array( $service, self::$services_load_check, true ) ) { // TODO - $valid_cloud_loads = array(); - foreach ($valid_clouds as $k => $v) { - $response = wp_safe_remote_get($v, array( 'timeout' => 5 )); - if (is_wp_error($response)) { + $valid_cloud_loads = []; + foreach ( $valid_clouds as $v ) { + $response = wp_safe_remote_get( $v, [ 'timeout' => 5 ] ); + if ( is_wp_error( $response ) ) { $error_message = $response->get_error_message(); - self::debug('failed to do load checker: ' . $error_message); + self::debug( 'failed to do load checker: ' . $error_message ); continue; } - $curr_load = \json_decode($response['body'], true); - if (!empty($curr_load['_res']) && $curr_load['_res'] == 'ok' && isset($curr_load['load'])) { - $valid_cloud_loads[$v] = $curr_load['load']; + $curr_load = \json_decode( $response['body'], true ); + if ( ! empty( $curr_load['_res'] ) && 'ok' === $curr_load['_res'] && isset( $curr_load['load'] ) ) { + $valid_cloud_loads[ $v ] = $curr_load['load']; } } - if (!$valid_cloud_loads) { - $msg = __('Cloud Error', 'litespeed-cache') . ": [Service] $service [Info] " . __('No available Cloud Node after checked server load.', 'litespeed-cache'); - Admin_Display::error($msg); + if ( ! $valid_cloud_loads ) { + $msg = __( 'Cloud Error', 'litespeed-cache' ) . ": [Service] $service [Info] " . __( 'No available Cloud Node after checked server load.', 'litespeed-cache' ); + Admin_Display::error( $msg ); return false; } - self::debug('Closest nodes list after load check', $valid_cloud_loads); + self::debug( 'Closest nodes list after load check', $valid_cloud_loads ); - $qualified_list = array_keys($valid_cloud_loads, min($valid_cloud_loads)); + $qualified_list = array_keys( $valid_cloud_loads, min( $valid_cloud_loads ), true ); } else { $qualified_list = $valid_clouds; } - $closest = $qualified_list[array_rand($qualified_list)]; + $closest = $qualified_list[ array_rand( $qualified_list ) ]; - self::debug('Chose node: ' . $closest); + self::debug( 'Chose node: ' . $closest ); // store data into option locally - $this->_summary['server.' . $service] = $closest; - $this->_summary['server_date.' . $service] = time(); + $this->_summary[ 'server.' . $service ] = $closest; + $this->_summary[ 'server_date.' . $service ] = time(); self::save_summary(); - return $this->_summary['server.' . $service]; + return $this->_summary[ 'server.' . $service ]; } /** * Ping to choose the closest nodes * * @since 7.0 + * + * @param array $nodes_list Node list. + * @param string $service Service. + * @return array|false */ - private function _get_closest_nodes( $list, $service ) { - $speed_list = array(); - foreach ($list as $v) { + private function _get_closest_nodes( $nodes_list, $service ) { + $speed_list = []; + foreach ( $nodes_list as $v ) { // Exclude possible failed 503 nodes - if (!empty($this->_summary['disabled_node']) && !empty($this->_summary['disabled_node'][$v]) && time() - $this->_summary['disabled_node'][$v] < 86400) { + if ( ! empty( $this->_summary['disabled_node'] ) && ! empty( $this->_summary['disabled_node'][ $v ] ) && time() - $this->_summary['disabled_node'][ $v ] < 86400 ) { continue; } - $speed_list[$v] = Utility::ping($v); + $speed_list[ $v ] = Utility::ping( $v ); } - if (!$speed_list) { - self::debug('nodes are in 503 failed nodes'); + if ( ! $speed_list ) { + self::debug( 'nodes are in 503 failed nodes' ); return false; } - $min = min($speed_list); + $min = min( $speed_list ); - if ($min == 99999) { - self::debug('failed to ping all clouds'); + if ( 99999 === (int) $min ) { + self::debug( 'failed to ping all clouds' ); return false; } // Random pick same time range ip (230ms 250ms) - $range_len = strlen($min); - $range_num = substr($min, 0, 1); - $valid_clouds = array(); - foreach ($speed_list as $node => $speed) { - if (strlen($speed) == $range_len && substr($speed, 0, 1) == $range_num) { + $range_len = strlen( $min ); + $range_num = substr( $min, 0, 1 ); + $valid_clouds = []; + foreach ( $speed_list as $node => $speed ) { + if ( strlen( $speed ) === $range_len && substr( $speed, 0, 1 ) === $range_num ) { $valid_clouds[] = $node; - } - // Append the lower speed ones - elseif ($speed < $min * 4) { + } elseif ( $speed < $min * 4 ) { // Append the lower speed ones $valid_clouds[] = $node; } } - if (!$valid_clouds) { - $msg = __('Cloud Error', 'litespeed-cache') . ": [Service] $service [Info] " . __('No available Cloud Node.', 'litespeed-cache'); - Admin_Display::error($msg); + if ( ! $valid_clouds ) { + $msg = __( 'Cloud Error', 'litespeed-cache' ) . ": [Service] $service [Info] " . __( 'No available Cloud Node.', 'litespeed-cache' ); + Admin_Display::error( $msg ); return false; } - self::debug('Closest nodes list', $valid_clouds); + self::debug( 'Closest nodes list', $valid_clouds ); return $valid_clouds; } /** * May need to convert to queue service + * + * @param string $service Service. + * @return string */ private function _maybe_queue( $service ) { - if (in_array($service, self::$_QUEUE_SVC_SET)) { + if ( in_array( $service, self::$_queue_svc_set, true ) ) { return self::SVC_QUEUE; } return $service; @@ -1109,175 +1259,225 @@ private function _maybe_queue( $service ) { /** * Get data from QUIC cloud server * - * @since 3.0 + * @since 3.0 * @access public + * + * @param string $service Service. + * @param array $data Data. + * @return mixed */ - public static function get( $service, $data = array() ) { + public static function get( $service, $data = [] ) { $instance = self::cls(); - return $instance->_get($service, $data); + return $instance->_get( $service, $data ); } /** - * Get data from QUIC cloud server + * Get data from QUIC cloud server (private) * - * @since 3.0 + * @since 3.0 * @access private + * + * @param string $service Service. + * @param array|bool $data Data array or false to omit. + * @return mixed */ private function _get( $service, $data = false ) { $service_tag = $service; - if (!empty($data['action'])) { + if ( ! empty( $data['action'] ) ) { $service_tag .= '-' . $data['action']; } - $maybe_cloud = $this->_maybe_cloud($service_tag); - if (!$maybe_cloud || $maybe_cloud === 'svc_hot') { + $maybe_cloud = $this->_maybe_cloud( $service_tag ); + if ( ! $maybe_cloud || 'svc_hot' === $maybe_cloud ) { return $maybe_cloud; } - $server = $this->detect_cloud($service); - if (!$server) { + $server = $this->detect_cloud( $service ); + if ( ! $server ) { return; } $url = $server . '/' . $service; - $param = array( - 'site_url' => site_url(), - 'main_domain' => !empty($this->_summary['main_domain']) ? $this->_summary['main_domain'] : '', - 'ver' => Core::VER, - ); + $param = [ + 'site_url' => site_url(), + 'main_domain'=> ! empty( $this->_summary['main_domain'] ) ? $this->_summary['main_domain'] : '', + 'ver' => Core::VER, + ]; - if ($data) { + if ( $data ) { $param['data'] = $data; } - $url .= '?' . http_build_query($param); + $url .= '?' . http_build_query( $param ); - self::debug('getting from : ' . $url); + self::debug( 'getting from : ' . $url ); - self::save_summary(array( 'curr_request.' . $service_tag => time() )); - file_put_contents(LITESPEED_STATIC_DIR . '/qc_curr_request' . md5($service_tag), time()); + self::save_summary( [ 'curr_request.' . $service_tag => time() ] ); + File::save( $this->_qc_time_file( $service_tag, 'curr' ), time(), true ); - $response = wp_safe_remote_get($url, array( - 'timeout' => 15, - 'headers' => array( 'Accept' => 'application/json' ), - )); + $response = wp_safe_remote_get( + $url, + [ + 'timeout' => 15, + 'headers' => [ 'Accept' => 'application/json' ], + ] + ); - return $this->_parse_response($response, $service, $service_tag, $server); + return $this->_parse_response( $response, $service, $service_tag, $server ); } /** * Check if is able to do cloud request or not * - * @since 3.0 + * @since 3.0 * @access private + * + * @param string $service_tag Service tag. + * @return bool|string */ private function _maybe_cloud( $service_tag ) { $site_url = site_url(); - if (!wp_http_validate_url($site_url)) { - self::debug('wp_http_validate_url failed: ' . $site_url); + if ( ! wp_http_validate_url( $site_url ) ) { + self::debug( 'wp_http_validate_url failed: ' . $site_url ); return false; } // Deny if is IP - if (preg_match('#^(([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)\.){3}([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)$#', Utility::parse_url_safe($site_url, PHP_URL_HOST))) { - self::debug('IP home url is not allowed for cloud service.'); - $msg = __('In order to use QC services, need a real domain name, cannot use an IP.', 'litespeed-cache'); - Admin_Display::error($msg); + if ( preg_match( '#^(([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)\.){3}([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)$#', Utility::parse_url_safe( $site_url, PHP_URL_HOST ) ) ) { + self::debug( 'IP home url is not allowed for cloud service.' ); + $msg = __( 'In order to use QC services, need a real domain name, cannot use an IP.', 'litespeed-cache' ); + Admin_Display::error( $msg ); return false; } - /** @since 5.0 If in valid err_domains, bypass request */ - if ($this->_is_err_domain($site_url)) { - self::debug('home url is in err_domains, bypass request: ' . $site_url); + // If in valid err_domains, bypass request + if ( $this->_is_err_domain( $site_url ) ) { + self::debug( 'home url is in err_domains, bypass request: ' . $site_url ); return false; } // we don't want the `img_optm-taken` to fail at any given time - if ($service_tag == self::IMGOPTM_TAKEN) { + if ( self::IMGOPTM_TAKEN === $service_tag ) { return true; } - if ($service_tag == self::SVC_D_SYNC_CONF && !$this->activated()) { - self::debug('Skip sync conf as QC not activated yet.'); + if ( self::SVC_D_SYNC_CONF === $service_tag && ! $this->activated() ) { + self::debug( 'Skip sync conf as QC not activated yet.' ); return false; } // Check TTL - if (!empty($this->_summary['ttl.' . $service_tag])) { - $ttl = $this->_summary['ttl.' . $service_tag] - time(); - if ($ttl > 0) { - self::debug('❌ TTL limit. [srv] ' . $service_tag . ' [TTL cool down] ' . $ttl . ' seconds'); + if ( ! empty( $this->_summary[ 'ttl.' . $service_tag ] ) ) { + $ttl = $this->_summary[ 'ttl.' . $service_tag ] - time(); + if ( $ttl > 0 ) { + self::debug( '❌ TTL limit. [srv] ' . $service_tag . ' [TTL cool down] ' . $ttl . ' seconds' ); return 'svc_hot'; } } $expiration_req = self::EXPIRATION_REQ; // Limit frequent unfinished request to 5min - $timestamp_tag = 'curr_request.'; - if ($service_tag == self::SVC_IMG_OPTM . '-' . Img_Optm::TYPE_NEW_REQ) { - $timestamp_tag = 'last_request.'; + $timestamp_tag = 'curr'; + if ( self::SVC_IMG_OPTM . '-' . Img_Optm::TYPE_NEW_REQ === $service_tag ) { + $timestamp_tag = 'last'; } // For all other requests, if is under debug mode, will always allow - if (!$this->conf(self::O_DEBUG)) { - if (!empty($this->_summary[$timestamp_tag . $service_tag])) { - $expired = $this->_summary[$timestamp_tag . $service_tag] + $expiration_req - time(); - if ($expired > 0) { - self::debug("❌ try [$service_tag] after $expired seconds"); + if ( ! $this->conf( self::O_DEBUG ) ) { + if ( ! empty( $this->_summary[ $timestamp_tag . '_request.' . $service_tag ] ) ) { + $expired = $this->_summary[ $timestamp_tag . '_request.' . $service_tag ] + $expiration_req - time(); + if ( $expired > 0 ) { + self::debug( '❌ try [' . $service_tag . '] after ' . $expired . ' seconds' ); - if ($service_tag !== self::API_VER) { + if ( self::API_VER !== $service_tag ) { $msg = - __('Cloud Error', 'litespeed-cache') . + __( 'Cloud Error', 'litespeed-cache' ) . ': ' . sprintf( - __('Please try after %1$s for service %2$s.', 'litespeed-cache'), - Utility::readable_time($expired, 0, true), + __( 'Please try after %1$s for service %2$s.', 'litespeed-cache' ), + Utility::readable_time( $expired, 0, true ), '<code>' . $service_tag . '</code>' ); - Admin_Display::error(array( 'cloud_trylater' => $msg )); + Admin_Display::error( [ 'cloud_trylater' => $msg ] ); } return false; } } else { - // May fail to store to db if db died. Need to store to file to prevent from duplicate calls - $file_path = LITESPEED_STATIC_DIR . '/qc_' . $timestamp_tag . md5($service_tag); - if (file_exists($file_path)) { - $last_request = file_get_contents($file_path); + // May fail to store to db if db is oc cached/dead/locked/readonly. Need to store to file to prevent from duplicate calls + $file_path = $this->_qc_time_file( $service_tag, $timestamp_tag ); + if ( file_exists( $file_path ) ) { + $last_request = File::read( $file_path ); $expired = $last_request + $expiration_req * 10 - time(); - if ($expired > 0) { - self::debug("❌ try [$service_tag] after $expired seconds"); + if ( $expired > 0 ) { + self::debug( '❌ try [' . $service_tag . '] after ' . $expired . ' seconds' ); return false; } } + // For ver check, additional check to prevent frequent calls as old DB ver may be cached + if ( self::API_VER === $service_tag ) { + $file_path = $this->_qc_time_file( $service_tag ); + if ( file_exists( $file_path ) ) { + $last_request = File::read( $file_path ); + $expired = $last_request + $expiration_req * 10 - time(); + if ( $expired > 0 ) { + self::debug( '❌❌ Unusual req! try [' . $service_tag . '] after ' . $expired . ' seconds' ); + return false; + } + } + } } } - if (in_array($service_tag, self::$_PUB_SVC_SET)) { + if ( in_array( $service_tag, self::$_pub_svc_set, true ) ) { return true; } - if (!$this->activated() && $service_tag != self::SVC_D_ACTIVATE) { - Admin_Display::error(Error::msg('qc_setup_required')); + if ( ! $this->activated() && self::SVC_D_ACTIVATE !== $service_tag ) { + Admin_Display::error( Error::msg( 'qc_setup_required' ) ); return false; } return true; } + /** + * Get QC req ts file path + * + * @since 7.5 + * + * @param string $service_tag Service tag. + * @param string $type Type: 'last' or 'curr'. + * @return string + */ + private function _qc_time_file( $service_tag, $type = 'last' ) { + if ( 'curr' !== $type ) { + $type = 'last'; + } + $legacy_file = LITESPEED_STATIC_DIR . '/qc_' . $type . '_request' . md5( $service_tag ); + if ( file_exists( $legacy_file ) ) { + wp_delete_file( $legacy_file ); + } + $service_tag = preg_replace( '/[^a-zA-Z0-9]/', '', $service_tag ); + return LITESPEED_STATIC_DIR . '/qc.' . $type . '.' . $service_tag; + } + /** * Check if a service tag ttl is valid or not * * @since 7.1 + * + * @param string $service_tag Service tag. + * @return int|false Seconds remaining or false if not hot. */ public function service_hot( $service_tag ) { - if (empty($this->_summary['ttl.' . $service_tag])) { + if ( empty( $this->_summary[ 'ttl.' . $service_tag ] ) ) { return false; } - $ttl = $this->_summary['ttl.' . $service_tag] - time(); - if ($ttl <= 0) { + $ttl = $this->_summary[ 'ttl.' . $service_tag ] - time(); + if ( $ttl <= 0 ) { return false; } @@ -1291,11 +1491,13 @@ public function service_hot( $service_tag ) { * @access public */ public function activated() { - return !empty($this->_summary['sk_b64']) && !empty($this->_summary['qc_activated']); + return ! empty( $this->_summary['sk_b64'] ) && ! empty( $this->_summary['qc_activated'] ); } /** * Show my.qc quick link to the domain page + * + * @return string */ public function qc_link() { $data = array( @@ -1303,7 +1505,7 @@ public function qc_link() { 'ver' => LSCWP_V, 'ref' => $this->_get_ref_url(), ); - return self::CLOUD_SERVER_DASH . '/u/wp3/manage?data=' . urlencode(Utility::arr2str($data)); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); + return $this->_cloud_server_dash . '/u/wp3/manage?data=' . rawurlencode( Utility::arr2str( $data ) ); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); } /** @@ -1311,10 +1513,15 @@ public function qc_link() { * * @since 3.0 * @access public + * + * @param string $service Service name/route. + * @param array|bool $data Payload data or false to omit. + * @param int|false $time_out Timeout seconds or false for default. + * @return mixed Response payload or false on failure. */ public static function post( $service, $data = false, $time_out = false ) { $instance = self::cls(); - return $instance->_post($service, $data, $time_out); + return $instance->_post( $service, $data, $time_out ); } /** @@ -1322,63 +1529,71 @@ public static function post( $service, $data = false, $time_out = false ) { * * @since 3.0 * @access private + * + * @param string $service Service name/route. + * @param array|bool $data Payload data or false to omit. + * @param int|false $time_out Timeout seconds or false for default. + * @return mixed Response payload or false on failure. */ private function _post( $service, $data = false, $time_out = false ) { $service_tag = $service; - if (!empty($data['action'])) { + if ( ! empty( $data['action'] ) ) { $service_tag .= '-' . $data['action']; } - $maybe_cloud = $this->_maybe_cloud($service_tag); - if (!$maybe_cloud || $maybe_cloud === 'svc_hot') { - self::debug('Maybe cloud failed: ' . var_export($maybe_cloud, true)); + $maybe_cloud = $this->_maybe_cloud( $service_tag ); + if ( ! $maybe_cloud || 'svc_hot' === $maybe_cloud ) { + self::debug( 'Maybe cloud failed: ' . wp_json_encode( $maybe_cloud ) ); return $maybe_cloud; } - $server = $this->detect_cloud($service); - if (!$server) { + $server = $this->detect_cloud( $service ); + if ( ! $server ) { return; } - $url = $server . '/' . $this->_maybe_queue($service); + $url = $server . '/' . $this->_maybe_queue( $service ); - self::debug('posting to : ' . $url); + self::debug( 'posting to : ' . $url ); - if ($data) { + if ( $data ) { $data['service_type'] = $service; // For queue distribution usage } // Encrypt service as signature // $signature_ts = time(); - // $sign_data = array( + // $sign_data = [ // 'service_tag' => $service_tag, // 'ts' => $signature_ts, - // ); + // ]; // $data['signature_b64'] = $this->_sign_b64(implode('', $sign_data)); // $data['signature_ts'] = $signature_ts; - self::debug('data', $data); - $param = array( - 'site_url' => site_url(), // Need to use site_url() as WPML case may change home_url() for diff langs (no need to treat as alias for multi langs) - 'main_domain' => !empty($this->_summary['main_domain']) ? $this->_summary['main_domain'] : '', - 'wp_pk_b64' => !empty($this->_summary['pk_b64']) ? $this->_summary['pk_b64'] : '', - 'ver' => Core::VER, - 'data' => $data, + self::debug( 'data', $data ); + $param = [ + 'site_url' => site_url(), // Need to use site_url() as WPML case may change home_url() for diff langs (no need to treat as alias for multi langs) + 'main_domain' => ! empty( $this->_summary['main_domain'] ) ? $this->_summary['main_domain'] : '', + 'wp_pk_b64' => ! empty( $this->_summary['pk_b64'] ) ? $this->_summary['pk_b64'] : '', + 'ver' => Core::VER, + 'data' => $data, + ]; + + self::save_summary( [ 'curr_request.' . $service_tag => time() ] ); + File::save( $this->_qc_time_file( $service_tag, 'curr' ), time(), true ); + + $response = wp_safe_remote_post( + $url, + [ + 'body' => $param, + 'timeout' => $time_out ?? 15, + 'headers' => [ + 'Accept' => 'application/json', + 'Expect' => '', + ], + ] ); - self::save_summary(array( 'curr_request.' . $service_tag => time() )); - file_put_contents(LITESPEED_STATIC_DIR . '/qc_curr_request' . md5($service_tag), time()); - - $response = wp_safe_remote_post($url, array( - 'body' => $param, - 'timeout' => $time_out ?: 15, - 'headers' => array( - 'Accept' => 'application/json', - 'Expect' => '', - ), - )); - - return $this->_parse_response($response, $service, $service_tag, $server); + return $this->_parse_response( $response, $service, $service_tag, $server ); } /** @@ -1386,130 +1601,136 @@ private function _post( $service, $data = false, $time_out = false ) { * Mark the request successful if the response status is ok * * @since 3.0 + * + * @param array|mixed $response WP HTTP API response. + * @param string $service Service name. + * @param string $service_tag Service tag including action. + * @param string $server Server URL. + * @return array|false Parsed JSON array or false on failure. */ private function _parse_response( $response, $service, $service_tag, $server ) { // If show the error or not if failed - $visible_err = $service !== self::API_VER && $service !== self::API_NEWS && $service !== self::SVC_D_DASH; + $visible_err = self::API_VER !== $service && self::API_NEWS !== $service && self::SVC_D_DASH !== $service; - if (is_wp_error($response)) { + if ( is_wp_error( $response ) ) { $error_message = $response->get_error_message(); - self::debug('failed to request: ' . $error_message); + self::debug( 'failed to request: ' . $error_message ); - if ($visible_err) { - $msg = __('Failed to request via WordPress', 'litespeed-cache') . ': ' . $error_message . " [server] $server [service] $service"; - Admin_Display::error($msg); + if ( $visible_err ) { + $msg = esc_html__( 'Failed to request via WordPress', 'litespeed-cache' ) . ': ' . esc_html( $error_message ) . ' [server] ' . esc_html( $server ) . ' [service] ' . esc_html( $service ); + Admin_Display::error( $msg ); // Tmp disabled this node from reusing in 1 day - if (empty($this->_summary['disabled_node'])) { - $this->_summary['disabled_node'] = array(); + if ( empty( $this->_summary['disabled_node'] ) ) { + $this->_summary['disabled_node'] = []; } - $this->_summary['disabled_node'][$server] = time(); + $this->_summary['disabled_node'][ $server ] = time(); self::save_summary(); // Force redetect node - self::debug('Node error, redetecting node [svc] ' . $service); - $this->detect_cloud($service, true); + self::debug( 'Node error, redetecting node [svc] ' . $service ); + $this->detect_cloud( $service, true ); } return false; } - $json = \json_decode($response['body'], true); + $json = \json_decode( $response['body'], true ); - if (!is_array($json)) { - self::debugErr('failed to decode response json: ' . $response['body']); + if ( ! is_array( $json ) ) { + self::debugErr( 'failed to decode response json: ' . $response['body'] ); - if ($visible_err) { - $msg = __('Failed to request via WordPress', 'litespeed-cache') . ': ' . $response['body'] . " [server] $server [service] $service"; - Admin_Display::error($msg); + if ( $visible_err ) { + $msg = esc_html__( 'Failed to request via WordPress', 'litespeed-cache' ) . ': ' . esc_html( $response['body'] ) . ' [server] ' . esc_html( $server ) . ' [service] ' . esc_html( $service ); + Admin_Display::error( $msg ); // Tmp disabled this node from reusing in 1 day - if (empty($this->_summary['disabled_node'])) { - $this->_summary['disabled_node'] = array(); + if ( empty( $this->_summary['disabled_node'] ) ) { + $this->_summary['disabled_node'] = []; } - $this->_summary['disabled_node'][$server] = time(); + $this->_summary['disabled_node'][ $server ] = time(); self::save_summary(); // Force redetect node - self::debugErr('Node error, redetecting node [svc] ' . $service); - $this->detect_cloud($service, true); + self::debugErr( 'Node error, redetecting node [svc] ' . $service ); + $this->detect_cloud( $service, true ); } return false; } // Check and save TTL data - if (!empty($json['_ttl'])) { - $ttl = intval($json['_ttl']); - self::debug('Service TTL to save: ' . $ttl); - if ($ttl > 0 && $ttl < 86400) { - self::save_summary(array( + if ( ! empty( $json['_ttl'] ) ) { + $ttl = (int) $json['_ttl']; + self::debug( 'Service TTL to save: ' . $ttl ); + if ( $ttl > 0 && $ttl < 86400 ) { + self::save_summary([ 'ttl.' . $service_tag => $ttl + time(), - )); + ]); } } - if (!empty($json['_code'])) { - self::debugErr('Hit err _code: ' . $json['_code']); - if ($json['_code'] == 'unpulled_images') { - $msg = __('Cloud server refused the current request due to unpulled images. Please pull the images first.', 'litespeed-cache'); - Admin_Display::error($msg); + if ( ! empty( $json['_code'] ) ) { + self::debugErr( 'Hit err _code: ' . $json['_code'] ); + if ( 'unpulled_images' === $json['_code'] ) { + $msg = __( 'Cloud server refused the current request due to unpulled images. Please pull the images first.', 'litespeed-cache' ); + Admin_Display::error( $msg ); return false; } - if ($json['_code'] == 'blocklisted') { - $msg = __('Your domain_key has been temporarily blocklisted to prevent abuse. You may contact support at QUIC.cloud to learn more.', 'litespeed-cache'); - Admin_Display::error($msg); + if ( 'blocklisted' === $json['_code'] ) { + $msg = __( 'Your domain_key has been temporarily blocklisted to prevent abuse. You may contact support at QUIC.cloud to learn more.', 'litespeed-cache' ); + Admin_Display::error( $msg ); return false; } - if ($json['_code'] == 'rate_limit') { - self::debugErr('Cloud server rate limit exceeded.'); - $msg = __('Cloud server refused the current request due to rate limiting. Please try again later.', 'litespeed-cache'); - Admin_Display::error($msg); + if ( 'rate_limit' === $json['_code'] ) { + self::debugErr( 'Cloud server rate limit exceeded.' ); + $msg = __( 'Cloud server refused the current request due to rate limiting. Please try again later.', 'litespeed-cache' ); + Admin_Display::error( $msg ); return false; } - if ($json['_code'] == 'heavy_load' || $json['_code'] == 'redetect_node') { + if ( 'heavy_load' === $json['_code'] || 'redetect_node' === $json['_code'] ) { // Force redetect node - self::debugErr('Node redetecting node [svc] ' . $service); - Admin_Display::info(__('Redetected node', 'litespeed-cache') . ': ' . Error::msg($json['_code'])); - $this->detect_cloud($service, true); + self::debugErr( 'Node redetecting node [svc] ' . $service ); + Admin_Display::info( __( 'Redetected node', 'litespeed-cache' ) . ': ' . Error::msg( $json['_code'] ) ); + $this->detect_cloud( $service, true ); } } - if (!empty($json['_503'])) { - self::debugErr('service 503 unavailable temporarily. ' . $json['_503']); + if ( ! empty( $json['_503'] ) ) { + self::debugErr( 'service 503 unavailable temporarily. ' . $json['_503'] ); $msg = __( 'We are working hard to improve your online service experience. The service will be unavailable while we work. We apologize for any inconvenience.', 'litespeed-cache' ); - $msg .= ' ' . $json['_503'] . " [server] $server [service] $service"; - Admin_Display::error($msg); + $msg .= ' ' . $json['_503'] . ' [server] ' . esc_html( $server ) . ' [service] ' . esc_html( $service ); + Admin_Display::error( $msg ); // Force redetect node - self::debugErr('Node error, redetecting node [svc] ' . $service); - $this->detect_cloud($service, true); + self::debugErr( 'Node error, redetecting node [svc] ' . $service ); + $this->detect_cloud( $service, true ); return false; } - list($json, $return) = $this->extract_msg($json, $service, $server); - if ($return) { + list( $json, $return ) = $this->extract_msg( $json, $service, $server ); + if ( $return ) { return false; } - $curr_request = $this->_summary['curr_request.' . $service_tag]; - self::save_summary(array( + $curr_request = $this->_summary[ 'curr_request.' . $service_tag ]; + self::save_summary([ 'last_request.' . $service_tag => $curr_request, 'curr_request.' . $service_tag => 0, - )); - file_put_contents(LITESPEED_STATIC_DIR . '/qc_last_request' . md5($service_tag), $curr_request); - file_put_contents(LITESPEED_STATIC_DIR . '/qc_curr_request' . md5($service_tag), 0); + ]); + File::save( $this->_qc_time_file( $service_tag ), $curr_request, true ); + File::save( $this->_qc_time_file( $service_tag, 'curr' ), 0, true ); - if ($json) { - self::debug2('response ok', $json); + if ( $json ) { + self::debug2( 'response ok', $json ); } else { - self::debug2('response ok'); + self::debug2( 'response ok' ); } // Only successful request return Array @@ -1520,86 +1741,91 @@ private function _parse_response( $response, $service, $service_tag, $server ) { * Extract msg from json * * @since 5.0 + * + * @param array $json Response JSON. + * @param string $service Service name. + * @param string|bool $server Server URL or false. + * @param bool $is_callback Whether called from callback context. + * @return array Array with [json array, bool should_return_false] */ public function extract_msg( $json, $service, $server = false, $is_callback = false ) { - if (!empty($json['_info'])) { - self::debug('_info: ' . $json['_info']); - $msg = __('Message from QUIC.cloud server', 'litespeed-cache') . ': ' . $json['_info']; - $msg .= $this->_parse_link($json); - Admin_Display::info($msg); - unset($json['_info']); + if ( ! empty( $json['_info'] ) ) { + self::debug( '_info: ' . $json['_info'] ); + $msg = __( 'Message from QUIC.cloud server', 'litespeed-cache' ) . ': ' . $json['_info']; + $msg .= $this->_parse_link( $json ); + Admin_Display::info( $msg ); + unset( $json['_info'] ); } - if (!empty($json['_note'])) { - self::debug('_note: ' . $json['_note']); - $msg = __('Message from QUIC.cloud server', 'litespeed-cache') . ': ' . $json['_note']; - $msg .= $this->_parse_link($json); - Admin_Display::note($msg); - unset($json['_note']); + if ( ! empty( $json['_note'] ) ) { + self::debug( '_note: ' . $json['_note'] ); + $msg = __( 'Message from QUIC.cloud server', 'litespeed-cache' ) . ': ' . $json['_note']; + $msg .= $this->_parse_link( $json ); + Admin_Display::note( $msg ); + unset( $json['_note'] ); } - if (!empty($json['_success'])) { - self::debug('_success: ' . $json['_success']); - $msg = __('Good news from QUIC.cloud server', 'litespeed-cache') . ': ' . $json['_success']; - $msg .= $this->_parse_link($json); - Admin_Display::success($msg); - unset($json['_success']); + if ( ! empty( $json['_success'] ) ) { + self::debug( '_success: ' . $json['_success'] ); + $msg = __( 'Good news from QUIC.cloud server', 'litespeed-cache' ) . ': ' . $json['_success']; + $msg .= $this->_parse_link( $json ); + Admin_Display::success( $msg ); + unset( $json['_success'] ); } // Upgrade is required - if (!empty($json['_err_req_v'])) { - self::debug('_err_req_v: ' . $json['_err_req_v']); - $msg = - sprintf(__('%1$s plugin version %2$s required for this action.', 'litespeed-cache'), Core::NAME, 'v' . $json['_err_req_v'] . '+') . - " [server] $server [service] $service"; + if ( ! empty( $json['_err_req_v'] ) ) { + self::debug( '_err_req_v: ' . $json['_err_req_v'] ); + $msg = sprintf( __( '%1$s plugin version %2$s required for this action.', 'litespeed-cache' ), Core::NAME, 'v' . $json['_err_req_v'] . '+' ) . + ' [server] ' . esc_html( $server ) . ' [service] ' . esc_html( $service ); // Append upgrade link - $msg2 = ' ' . GUI::plugin_upgrade_link(Core::NAME, Core::PLUGIN_NAME, $json['_err_req_v']); + $msg2 = ' ' . GUI::plugin_upgrade_link( Core::NAME, Core::PLUGIN_NAME, $json['_err_req_v'] ); - $msg2 .= $this->_parse_link($json); - Admin_Display::error($msg . $msg2); - return array( $json, true ); + $msg2 .= $this->_parse_link( $json ); + Admin_Display::error( $msg . $msg2 ); + return [ $json, true ]; } // Parse _carry_on info - if (!empty($json['_carry_on'])) { - self::debug('Carry_on usage', $json['_carry_on']); + if ( ! empty( $json['_carry_on'] ) ) { + self::debug( 'Carry_on usage', $json['_carry_on'] ); // Store generic info - foreach (array( 'usage', 'promo', 'mini_html', 'partner', '_error', '_info', '_note', '_success' ) as $v) { - if (isset($json['_carry_on'][$v])) { - switch ($v) { + foreach ( [ 'usage', 'promo', 'mini_html', 'partner', '_error', '_info', '_note', '_success' ] as $v ) { + if ( isset( $json['_carry_on'][ $v ] ) ) { + switch ( $v ) { case 'usage': - $usage_svc_tag = in_array($service, array( self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI )) ? self::SVC_PAGE_OPTM : $service; - $this->_summary['usage.' . $usage_svc_tag] = $json['_carry_on'][$v]; + $usage_svc_tag = in_array( $service, [ self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI ], true ) ? self::SVC_PAGE_OPTM : $service; + $this->_summary[ 'usage.' . $usage_svc_tag ] = $json['_carry_on'][ $v ]; break; case 'promo': - if (empty($this->_summary[$v]) || !is_array($this->_summary[$v])) { - $this->_summary[$v] = array(); + if ( empty( $this->_summary[ $v ] ) || ! is_array( $this->_summary[ $v ] ) ) { + $this->_summary[ $v ] = []; } - $this->_summary[$v][] = $json['_carry_on'][$v]; + $this->_summary[ $v ][] = $json['_carry_on'][ $v ]; break; case 'mini_html': - foreach ($json['_carry_on'][$v] as $k2 => $v2) { - if (strpos($k2, 'ttl.') === 0) { + foreach ( $json['_carry_on'][ $v ] as $k2 => $v2 ) { + if ( 0 === strpos( $k2, 'ttl.' ) ) { $v2 += time(); } - $this->_summary[$v][$k2] = $v2; + $this->_summary[ $v ][ $k2 ] = $v2; } break; case 'partner': - $this->_summary[$v] = $json['_carry_on'][$v]; + $this->_summary[ $v ] = $json['_carry_on'][ $v ]; break; case '_error': case '_info': case '_note': case '_success': - $color_mode = substr($v, 1); - $msgs = $json['_carry_on'][$v]; - Admin_Display::add_unique_notice($color_mode, $msgs, true); + $color_mode = substr( $v, 1 ); + $msgs = $json['_carry_on'][ $v ]; + Admin_Display::add_unique_notice( $color_mode, $msgs, true ); break; default: @@ -1608,46 +1834,46 @@ public function extract_msg( $json, $service, $server = false, $is_callback = fa } } self::save_summary(); - unset($json['_carry_on']); + unset( $json['_carry_on'] ); } // Parse general error msg - if (!$is_callback && (empty($json['_res']) || $json['_res'] !== 'ok')) { - $json_msg = !empty($json['_msg']) ? $json['_msg'] : 'unknown'; - self::debug('❌ _err: ' . $json_msg, $json); - - $str_translated = Error::msg($json_msg); - $msg = __('Failed to communicate with QUIC.cloud server', 'litespeed-cache') . ': ' . $str_translated . " [server] $server [service] $service"; - $msg .= $this->_parse_link($json); - $visible_err = $service !== self::API_VER && $service !== self::API_NEWS && $service !== self::SVC_D_DASH; - if ($visible_err) { - Admin_Display::error($msg); + if ( ! $is_callback && ( empty( $json['_res'] ) || 'ok' !== $json['_res'] ) ) { + $json_msg = ! empty( $json['_msg'] ) ? $json['_msg'] : 'unknown'; + self::debug( '❌ _err: ' . $json_msg, $json ); + + $str_translated = Error::msg( $json_msg ); + $msg = __( 'Failed to communicate with QUIC.cloud server', 'litespeed-cache' ) . ': ' . $str_translated . ' [server] ' . esc_html( $server ) . ' [service] ' . esc_html( $service ); + $msg .= $this->_parse_link( $json ); + $visible_err = self::API_VER !== $service && self::API_NEWS !== $service && self::SVC_D_DASH !== $service; + if ( $visible_err ) { + Admin_Display::error( $msg ); } // QC may try auto alias - /** @since 5.0 Store the domain as `err_domains` only for QC auto alias feature */ - if ($json_msg == 'err_alias') { - if (empty($this->_summary['err_domains'])) { - $this->_summary['err_domains'] = array(); + // Store the domain as `err_domains` only for QC auto alias feature + if ( 'err_alias' === $json_msg ) { + if ( empty( $this->_summary['err_domains'] ) ) { + $this->_summary['err_domains'] = []; } $site_url = site_url(); - if (!array_key_exists($site_url, $this->_summary['err_domains'])) { - $this->_summary['err_domains'][$site_url] = time(); + if ( ! array_key_exists( $site_url, $this->_summary['err_domains'] ) ) { + $this->_summary['err_domains'][ $site_url ] = time(); } self::save_summary(); } - // Site not on QC, delete invalid domain key - if ($json_msg == 'site_not_registered' || $json_msg == 'err_key') { + // Site not on QC, reset QC connection registration + if ( 'site_not_registered' === $json_msg || 'err_key' === $json_msg ) { $this->_reset_qc_reg(); } return array( $json, true ); } - unset($json['_res']); - if (!empty($json['_msg'])) { - unset($json['_msg']); + unset( $json['_res'] ); + if ( ! empty( $json['_msg'] ) ) { + unset( $json['_msg'] ); } return array( $json, false ); @@ -1659,27 +1885,38 @@ public function extract_msg( $json, $service, $server = false, $is_callback = fa * @since 5.0 */ private function _reset_qc_reg() { - unset($this->_summary['qc_activated']); - if (!empty($this->_summary['partner'])) { - unset($this->_summary['partner']); + unset( $this->_summary['qc_activated'] ); + if ( ! empty( $this->_summary['partner'] ) ) { + unset( $this->_summary['partner'] ); } self::save_summary(); $msg = $this->_reset_qc_reg_content(); - Admin_Display::error($msg, false, true); + Admin_Display::error( $msg, false, true ); } + /** + * Build reset QC registration content. + * + * @since 7.0 + * @return string + */ private function _reset_qc_reg_content() { - $msg = __('Site not recognized. QUIC.cloud deactivated automatically. Please reactivate your QUIC.cloud account.', 'litespeed-cache'); - $msg .= Doc::learn_more(admin_url('admin.php?page=litespeed'), __('Click here to proceed.', 'litespeed-cache'), true, false, true); - $msg .= Doc::learn_more('https://docs.litespeedtech.com/lscache/lscwp/general/', false, false, false, true); + $msg = __( 'Site not recognized. QUIC.cloud deactivated automatically. Please reactivate your QUIC.cloud account.', 'litespeed-cache' ); + $msg .= Doc::learn_more( admin_url( 'admin.php?page=litespeed' ), __( 'Click here to proceed.', 'litespeed-cache' ), true, false, true ); + $msg .= Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/general/', false, false, false, true ); return $msg; } + /** + * Clear reset QC reg msg if exist + * + * @since 7.0 + */ private function _clear_reset_qc_reg_msg() { - self::debug('Removed pinned reset QC reg content msg'); + self::debug( 'Removed pinned reset QC reg content msg' ); $msg = $this->_reset_qc_reg_content(); - Admin_Display::dismiss_pin_by_content($msg, Admin_Display::NOTICE_RED, true); + Admin_Display::dismiss_pin_by_content( $msg, Admin_Display::NOTICE_RED, true ); } /** @@ -1688,29 +1925,35 @@ private function _clear_reset_qc_reg_msg() { * @since 5.0 */ public function rest_err_domains() { - if (empty($_POST['main_domain']) || empty($_POST['alias'])) { - return self::err('lack_of_param'); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $alias = !empty( $_POST['alias'] ) ? sanitize_text_field( wp_unslash( $_POST['alias'] ) ) : ''; + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( empty( $_POST['main_domain'] ) || !$alias ) { + return self::err( 'lack_of_param' ); } - $this->extract_msg($_POST, 'Quic.cloud', false, true); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $this->extract_msg( $_POST, 'Quic.cloud', false, true ); - if ($this->_is_err_domain($_POST['alias'])) { - if ($_POST['alias'] == site_url()) { - $this->_remove_domain_from_err_list($_POST['alias']); + if ( $this->_is_err_domain( $alias ) ) { + if ( site_url() === $alias ) { + $this->_remove_domain_from_err_list( $alias ); } return self::ok(); } - return self::err('Not an alias req from here'); + return self::err( 'Not an alias req from here' ); } /** * Remove a domain from err domain * * @since 5.0 + * + * @param string $url URL to remove. */ private function _remove_domain_from_err_list( $url ) { - unset($this->_summary['err_domains'][$url]); + unset( $this->_summary['err_domains'][ $url ] ); self::save_summary(); } @@ -1718,21 +1961,24 @@ private function _remove_domain_from_err_list( $url ) { * Check if is err domain * * @since 5.0 + * + * @param string $site_url Site URL. + * @return bool */ private function _is_err_domain( $site_url ) { - if (empty($this->_summary['err_domains'])) { + if ( empty( $this->_summary['err_domains'] ) ) { return false; } - if (!array_key_exists($site_url, $this->_summary['err_domains'])) { + if ( ! array_key_exists( $site_url, $this->_summary['err_domains'] ) ) { return false; } // Auto delete if too long ago - if (time() - $this->_summary['err_domains'][$site_url] > 86400 * 10) { - $this->_remove_domain_from_err_list($site_url); + if ( time() - $this->_summary['err_domains'][ $site_url ] > 86400 * 10 ) { + $this->_remove_domain_from_err_list( $site_url ); return false; } - if (time() - $this->_summary['err_domains'][$site_url] > 86400) { + if ( time() - $this->_summary['err_domains'][ $site_url ] > 86400 ) { return false; } return true; @@ -1745,7 +1991,7 @@ private function _is_err_domain( $site_url ) { * @access public */ public function show_promo() { - if (empty($this->_summary['promo'])) { + if ( empty( $this->_summary['promo'] ) ) { return; } @@ -1759,10 +2005,10 @@ public function show_promo() { * @access private */ private function _clear_promo() { - if (count($this->_summary['promo']) > 1) { - array_shift($this->_summary['promo']); + if ( count( $this->_summary['promo'] ) > 1 ) { + array_shift( $this->_summary['promo'] ); } else { - $this->_summary['promo'] = array(); + $this->_summary['promo'] = []; } self::save_summary(); } @@ -1773,16 +2019,19 @@ private function _clear_promo() { * @since 1.6.5 * @since 1.6.7 Self clean the parameter * @access private + * + * @param array $json JSON array (passed by reference). + * @return string HTML link string. */ private function _parse_link( &$json ) { $msg = ''; - if (!empty($json['_links'])) { - foreach ($json['_links'] as $v) { - $msg .= ' ' . sprintf('<a href="%s" class="%s" target="_blank">%s</a>', $v['link'], !empty($v['cls']) ? $v['cls'] : '', $v['title']); + if ( ! empty( $json['_links'] ) ) { + foreach ( $json['_links'] as $v ) { + $msg .= ' ' . sprintf( '<a href="%s" class="%s" target="_blank">%s</a>', esc_url( $v['link'] ), ! empty( $v['cls'] ) ? esc_attr( $v['cls'] ) : '', esc_html( $v['title'] ) ); } - unset($json['_links']); + unset( $json['_links'] ); } return $msg; @@ -1795,22 +2044,24 @@ private function _parse_link( &$json ) { * @access public */ public function ip_validate() { - if (empty($_POST['hash'])) { - return self::err('lack_of_params'); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $hash = ! empty( $_POST['hash'] ) ? sanitize_text_field( wp_unslash( $_POST['hash'] ) ) : ''; + if ( !$hash ) { + return self::err( 'lack_of_params' ); } - if ($_POST['hash'] != md5(substr($this->_summary['pk_b64'], 0, 4))) { - self::debug('__callback IP request decryption failed'); - return self::err('err_hash'); + if ( md5( substr( $this->_summary['pk_b64'], 0, 4 ) ) !== $hash ) { + self::debug( '__callback IP request decryption failed' ); + return self::err( 'err_hash' ); } - Control::set_nocache('Cloud IP hash validation'); + Control::set_nocache( 'Cloud IP hash validation' ); - $resp_hash = md5(substr($this->_summary['pk_b64'], 2, 4)); + $resp_hash = md5( substr( $this->_summary['pk_b64'], 2, 4 ) ); - self::debug('__callback IP request hash: ' . $resp_hash); + self::debug( '__callback IP request hash: ' . $resp_hash ); - return self::ok(array( 'hash' => $resp_hash )); + return self::ok( array( 'hash' => $resp_hash ) ); } /** @@ -1821,30 +2072,30 @@ public function ip_validate() { public function is_from_cloud() { // return true; $check_point = time() - 86400 * self::TTL_IPS; - if (empty($this->_summary['ips']) || empty($this->_summary['ips_ts']) || $this->_summary['ips_ts'] < $check_point) { - self::debug('Force updating ip as ips_ts is older than ' . self::TTL_IPS . ' days'); + if ( empty( $this->_summary['ips'] ) || empty( $this->_summary['ips_ts'] ) || $this->_summary['ips_ts'] < $check_point ) { + self::debug( 'Force updating ip as ips_ts is older than ' . self::TTL_IPS . ' days' ); $this->_update_ips(); } - $res = $this->cls('Router')->ip_access($this->_summary['ips']); - if (!$res) { - self::debug('❌ Not our cloud IP'); + $res = $this->cls( 'Router' )->ip_access( $this->_summary['ips'] ); + if ( ! $res ) { + self::debug( '❌ Not our cloud IP' ); // Auto check ip list again but need an interval limit safety. - if (empty($this->_summary['ips_ts_runner']) || time() - $this->_summary['ips_ts_runner'] > 600) { - self::debug('Force updating ip as ips_ts_runner is older than 10mins'); + if ( empty( $this->_summary['ips_ts_runner'] ) || time() - $this->_summary['ips_ts_runner'] > 600 ) { + self::debug( 'Force updating ip as ips_ts_runner is older than 10mins' ); // Refresh IP list for future detection $this->_update_ips(); - $res = $this->cls('Router')->ip_access($this->_summary['ips']); - if (!$res) { - self::debug('❌ 2nd time: Not our cloud IP'); + $res = $this->cls( 'Router' )->ip_access( $this->_summary['ips'] ); + if ( ! $res ) { + self::debug( '❌ 2nd time: Not our cloud IP' ); } else { - self::debug('✅ Passed Cloud IP verification'); + self::debug( '✅ Passed Cloud IP verification' ); } return $res; } } else { - self::debug('✅ Passed Cloud IP verification'); + self::debug( '✅ Passed Cloud IP verification' ); } return $res; @@ -1854,34 +2105,39 @@ public function is_from_cloud() { * Update Cloud IP list * * @since 4.2 + * + * @throws \Exception When fetching whitelist fails. */ private function _update_ips() { - self::debug('Load remote Cloud IP list from ' . self::CLOUD_IPS); + self::debug( 'Load remote Cloud IP list from ' . $this->_cloud_ips ); // Prevent multiple call in a short period - self::save_summary(array( - 'ips_ts' => time(), - 'ips_ts_runner' => time(), - )); + self::save_summary([ + 'ips_ts' => time(), + 'ips_ts_runner' => time(), + ]); - $response = wp_safe_remote_get(self::CLOUD_IPS . '?json'); - if (is_wp_error($response)) { + $response = wp_safe_remote_get( $this->_cloud_ips . '?json' ); + if ( is_wp_error( $response ) ) { $error_message = $response->get_error_message(); - self::debug('failed to get ip whitelist: ' . $error_message); - throw new \Exception('Failed to fetch QUIC.cloud whitelist ' . $error_message); + self::debug( 'failed to get ip whitelist: ' . $error_message ); + throw new \Exception( 'Failed to fetch QUIC.cloud whitelist ' . esc_html($error_message) ); } - $json = \json_decode($response['body'], true); + $json = \json_decode( $response['body'], true ); - self::debug('Load ips', $json); - self::save_summary(array( 'ips' => $json )); + self::debug( 'Load ips', $json ); + self::save_summary( array( 'ips' => $json ) ); } /** * Return succeeded response * * @since 3.0 + * + * @param array $data Additional data. + * @return array */ - public static function ok( $data = array() ) { + public static function ok( $data = [] ) { $data['_res'] = 'ok'; return $data; } @@ -1890,9 +2146,12 @@ public static function ok( $data = array() ) { * Return error * * @since 3.0 + * + * @param string $code Error code. + * @return array */ public static function err( $code ) { - self::debug("❌ Error response code: $code"); + self::debug( '❌ Error response code: ' . $code ); return array( '_res' => 'err', '_msg' => $code, @@ -1903,32 +2162,40 @@ public static function err( $code ) { * Return pong for ping to check PHP function availability * * @since 6.5 + * + * @return array */ public function ping() { $resp = array( - 'v_lscwp' => Core::VER, - 'v_lscwp_db' => $this->conf(self::_VER), - 'v_php' => PHP_VERSION, - 'v_wp' => $GLOBALS['wp_version'], - 'home_url' => home_url(), - 'site_url' => site_url(), + 'v_lscwp' => Core::VER, + 'v_lscwp_db' => $this->conf( self::_VER ), + 'v_php' => PHP_VERSION, + 'v_wp' => $GLOBALS['wp_version'], + 'home_url' => home_url(), + 'site_url' => site_url(), ); - if (!empty($_POST['funcs'])) { - foreach ($_POST['funcs'] as $v) { - $resp[$v] = function_exists($v) ? 'y' : 'n'; + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( ! empty( $_POST['funcs'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + foreach ( wp_unslash($_POST['funcs']) as $v ) { + $resp[ $v ] = function_exists( $v ) ? 'y' : 'n'; } } - if (!empty($_POST['classes'])) { - foreach ($_POST['classes'] as $v) { - $resp[$v] = class_exists($v) ? 'y' : 'n'; + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( ! empty( $_POST['classes'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + foreach ( wp_unslash($_POST['classes']) as $v ) { + $resp[ $v ] = class_exists( $v ) ? 'y' : 'n'; } } - if (!empty($_POST['consts'])) { - foreach ($_POST['consts'] as $v) { - $resp[$v] = defined($v) ? 'y' : 'n'; + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( ! empty( $_POST['consts'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + foreach ( wp_unslash($_POST['consts']) as $v ) { + $resp[ $v ] = defined( $v ) ? 'y' : 'n'; } } - return self::ok($resp); + return self::ok( $resp ); } /** @@ -1937,8 +2204,8 @@ public function ping() { * @since 7.0 */ public function maybe_preview_banner() { - if (strpos(self::CLOUD_SERVER, 'preview.')) { - Admin_Display::note(__('Linked to QUIC.cloud preview environment, for testing purpose only.', 'litespeed-cache'), true, true, 'litespeed-warning-bg'); + if ( false !== strpos( $this->_cloud_server, 'preview.' ) ) { + Admin_Display::note( __( 'Linked to QUIC.cloud preview environment, for testing purpose only.', 'litespeed-cache' ), true, true, 'litespeed-warning-bg' ); } } @@ -1951,14 +2218,16 @@ public function maybe_preview_banner() { public function handler() { $type = Router::verify_type(); - switch ($type) { + switch ( $type ) { case self::TYPE_CLEAR_CLOUD: $this->clear_cloud(); break; case self::TYPE_REDETECT_CLOUD: - if (!empty($_GET['svc'])) { - $this->detect_cloud($_GET['svc'], true); + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $svc = ! empty( $_GET['svc'] ) ? sanitize_text_field( wp_unslash( $_GET['svc'] ) ) : ''; + if ( $svc ) { + $this->detect_cloud( $svc, true ); } break; @@ -1983,22 +2252,24 @@ public function handler() { break; case self::TYPE_API: - if (!empty($_GET['action2'])) { - $this->api_link_call($_GET['action2']); + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $action2 = ! empty( $_GET['action2'] ) ? sanitize_text_field( wp_unslash( $_GET['action2'] ) ) : ''; + if ( $action2 ) { + $this->api_link_call( $action2 ); } break; case self::TYPE_SYNC_STATUS: - $this->load_qc_status_for_dash('cdn_dash', true); - $msg = __('Sync QUIC.cloud status successfully.', 'litespeed-cache'); - Admin_Display::success($msg); + $this->load_qc_status_for_dash( 'cdn_dash', true ); + $msg = __( 'Sync QUIC.cloud status successfully.', 'litespeed-cache' ); + Admin_Display::success( $msg ); break; case self::TYPE_SYNC_USAGE: $this->sync_usage(); - $msg = __('Sync credit allowance with Cloud Server successfully.', 'litespeed-cache'); - Admin_Display::success($msg); + $msg = __( 'Sync credit allowance with Cloud Server successfully.', 'litespeed-cache' ); + Admin_Display::success( $msg ); break; default: diff --git a/src/conf.cls.php b/src/conf.cls.php index 6a2595394..2cea81fe5 100644 --- a/src/conf.cls.php +++ b/src/conf.cls.php @@ -1,33 +1,47 @@ <?php - /** * The core plugin config class. * * This maintains all the options and settings for this plugin. * * @since 1.0.0 - * @since 1.5 Moved into /inc * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; defined('WPINC') || exit(); +/** + * Class Conf + * + * Maintains all LiteSpeed plugin configuration, including CRUD for single-site + * and multisite options, upgrade flows, and side effects like purging/cron. + */ class Conf extends Base { const TYPE_SET = 'set'; - private $_updated_ids = array(); - private $_is_primary = false; + /** + * IDs that were updated during a save cycle. + * + * @var array<string|int,mixed> + */ + private $_updated_ids = []; + + /** + * Whether current blog is the network primary site. + * + * @var bool + */ + private $_is_primary = false; /** * Specify init logic to avoid infinite loop when calling conf.cls instance * * @since 3.0 * @access public + * @return void */ public function init() { // Check if conf exists or not. If not, create them in DB (won't change version if is converting v2.9- data) @@ -39,12 +53,14 @@ public function init() { * * @since 2.9.7 */ - if ($this->conf(self::O_CDN_QUIC)) { - !defined('LITESPEED_ALLOWED') && define('LITESPEED_ALLOWED', true); + if ( $this->conf( self::O_CDN_QUIC ) ) { + if ( ! defined( 'LITESPEED_ALLOWED' ) ) { + define( 'LITESPEED_ALLOWED', true ); + } } - add_action('litespeed_conf_append', array( $this, 'option_append' ), 10, 2); - add_action('litespeed_conf_force', array( $this, 'force_option' ), 10, 2); + add_action( 'litespeed_conf_append', [ $this, 'option_append' ], 10, 2 ); + add_action( 'litespeed_conf_force', [ $this, 'force_option' ], 10, 2 ); $this->define_cache(); } @@ -54,6 +70,7 @@ public function init() { * * @since 3.0 * @access private + * @return void */ private function _conf_db_init() { /** @@ -65,57 +82,57 @@ private function _conf_db_init() { // Check if debug is on // Init debug as early as possible - if ($this->conf(Base::O_DEBUG)) { - $this->cls('Debug2')->init(); + if ( $this->conf( Base::O_DEBUG ) ) { + $this->cls( 'Debug2' )->init(); } - $ver = $this->conf(self::_VER); + $ver = $this->conf( self::_VER ); /** * Version is less than v3.0, or, is a new installation */ - $ver_check_tag = ''; - if (!$ver) { - // Try upgrade first (network will upgrade inside too) - $ver_check_tag = Data::cls()->try_upgrade_conf_3_0(); - } else { - defined('LSCWP_CUR_V') || define('LSCWP_CUR_V', $ver); + $ver_check_tag = 'new'; + if ( $ver ) { + if ( ! defined( 'LSCWP_CUR_V' ) ) { + define( 'LSCWP_CUR_V', $ver ); + } /** * Upgrade conf */ - if ($ver != Core::VER) { + if ( Core::VER !== $ver ) { // Plugin version will be set inside // Site plugin upgrade & version change will do in load_site_conf - $ver_check_tag = Data::cls()->conf_upgrade($ver); + $ver_check_tag = Data::cls()->conf_upgrade( $ver ); } } /** * Sync latest new options */ - if (!$ver || $ver != Core::VER) { + if ( ! $ver || Core::VER !== $ver ) { // Load default values $this->load_default_vals(); - if (!$ver) { + + if ( ! $ver ) { // New install - $this->set_conf(self::$_default_options); + $this->set_conf( self::$_default_options ); - $ver_check_tag .= ' activate' . (defined('LSCWP_REF') ? '_' . LSCWP_REF : ''); + $ver_check_tag .= ' activate' . ( defined( 'LSCWP_REF' ) ? '_' . constant( 'LSCWP_REF' ) : '' ); } // Init new default/missing options - foreach (self::$_default_options as $k => $v) { + foreach ( self::$_default_options as $k => $v ) { // If the option existed, bypass updating // Bcos we may ask clients to deactivate for debug temporarily, we need to keep the current cfg in deactivation, hence we need to only try adding default cfg when activating. - self::add_option($k, $v); + self::add_option( $k, $v ); } // Force correct version in case a rare unexpected case that `_ver` exists but empty - self::update_option(Base::_VER, Core::VER); + self::update_option( Base::_VER, Core::VER ); - if ($ver_check_tag) { - Cloud::version_check($ver_check_tag); + if ( $ver_check_tag ) { + Cloud::version_check( $ver_check_tag ); } } @@ -128,14 +145,16 @@ private function _conf_db_init() { // Check if debug is on // Init debug as early as possible - if ($this->conf(Base::O_DEBUG)) { - $this->cls('Debug2')->init(); + if ( $this->conf( Base::O_DEBUG ) ) { + $this->cls( 'Debug2' )->init(); } // Mark as conf loaded - defined('LITESPEED_CONF_LOADED') || define('LITESPEED_CONF_LOADED', true); + if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) { + define( 'LITESPEED_CONF_LOADED', true ); + } - if (!$ver || $ver != Core::VER) { + if ( ! $ver || Core::VER !== $ver ) { // Only trigger once in upgrade progress, don't run always $this->update_confs(); // Files only get corrected in activation or saving settings actions. } @@ -146,41 +165,45 @@ private function _conf_db_init() { * * @since 3.0 * @access public + * + * @param int|null $blog_id Blog ID to load from. Null for current. + * @param bool $dry_run Return options instead of setting them. + * @return array<string,mixed>|void */ public function load_options( $blog_id = null, $dry_run = false ) { - $options = array(); - foreach (self::$_default_options as $k => $v) { - if (!is_null($blog_id)) { - $options[$k] = self::get_blog_option($blog_id, $k, $v); + $options = []; + foreach ( self::$_default_options as $k => $v ) { + if ( null !== $blog_id ) { + $options[ $k ] = self::get_blog_option( $blog_id, $k, $v ); } else { - $options[$k] = self::get_option($k, $v); + $options[ $k ] = self::get_option( $k, $v ); } // Correct value type - $options[$k] = $this->type_casting($options[$k], $k); + $options[ $k ] = $this->type_casting( $options[ $k ], $k ); } - if ($dry_run) { + if ( $dry_run ) { return $options; } // Bypass site special settings - if ($blog_id !== null) { + if ( null !== $blog_id ) { // This is to load the primary settings ONLY // These options are the ones that can be overwritten by primary - $options = array_diff_key($options, array_flip(self::$SINGLE_SITE_OPTIONS)); + $options = array_diff_key( $options, array_flip( self::$single_site_options ) ); - $this->set_primary_conf($options); + $this->set_primary_conf( $options ); } else { - $this->set_conf($options); + $this->set_conf( $options ); } // Append const options - if (defined('LITESPEED_CONF') && LITESPEED_CONF) { - foreach (self::$_default_options as $k => $v) { - $const = Base::conf_const($k); - if (defined($const)) { - $this->set_const_conf($k, $this->type_casting(constant($const), $k)); + if ( defined( 'LITESPEED_CONF' ) && LITESPEED_CONF ) { + foreach ( self::$_default_options as $k => $v ) { + $const = Base::conf_const( $k ); + if ( defined( $const ) ) { + $this->set_const_conf( $k, $this->type_casting( constant( $const ), $k ) ); } } } @@ -191,51 +214,52 @@ public function load_options( $blog_id = null, $dry_run = false ) { * * @since 1.0.13 * @access private + * @return void */ private function _try_load_site_options() { - if (!$this->_if_need_site_options()) { + if ( ! $this->_if_need_site_options() ) { return; } $this->_conf_site_db_init(); - $this->_is_primary = get_current_blog_id() == BLOG_ID_CURRENT_SITE; + $this->_is_primary = BLOG_ID_CURRENT_SITE === get_current_blog_id(); // If network set to use primary setting - if ($this->network_conf(self::NETWORK_O_USE_PRIMARY) && !$this->_is_primary) { + if ( $this->network_conf( self::NETWORK_O_USE_PRIMARY ) && ! $this->_is_primary ) { // subsites or network admin // Get the primary site settings // If it's just upgraded, 2nd blog is being visited before primary blog, can just load default config (won't hurt as this could only happen shortly) - $this->load_options(BLOG_ID_CURRENT_SITE); + $this->load_options( BLOG_ID_CURRENT_SITE ); } // Overwrite single blog options with site options - foreach (self::$_default_options as $k => $v) { - if (!$this->has_network_conf($k)) { + foreach ( self::$_default_options as $k => $v ) { + if ( ! $this->has_network_conf( $k ) ) { continue; } // $this->_options[ $k ] = $this->_network_options[ $k ]; // Special handler to `Enable Cache` option if the value is set to OFF - if ($k == self::O_CACHE) { - if ($this->_is_primary) { - if ($this->conf($k) != $this->network_conf($k)) { - if ($this->conf($k) != self::VAL_ON2) { + if ( self::O_CACHE === $k ) { + if ( $this->_is_primary ) { + if ( $this->conf( $k ) !== $this->network_conf( $k ) ) { + if ( self::VAL_ON2 !== $this->conf( $k ) ) { continue; } } - } elseif ($this->network_conf(self::NETWORK_O_USE_PRIMARY)) { - if ($this->has_primary_conf($k) && $this->primary_conf($k) != self::VAL_ON2) { + } elseif ( $this->network_conf( self::NETWORK_O_USE_PRIMARY ) ) { + if ( $this->has_primary_conf( $k ) && self::VAL_ON2 !== $this->primary_conf( $k ) ) { // This case will use primary_options override always continue; } - } elseif ($this->conf($k) != self::VAL_ON2) { + } elseif ( self::VAL_ON2 !== $this->conf( $k ) ) { continue; } } // primary_options will store primary settings + network settings, OR, store the network settings for subsites - $this->set_primary_conf($k, $this->network_conf($k)); + $this->set_primary_conf( $k, $this->network_conf( $k ) ); } // var_dump($this->_options); } @@ -245,9 +269,10 @@ private function _try_load_site_options() { * * @since 3.0 * @access private + * @return bool */ private function _if_need_site_options() { - if (!is_multisite()) { + if ( ! is_multisite() ) { return false; } @@ -260,14 +285,14 @@ private function _if_need_site_options() { * @see https://codex.wordpress.org/Function_Reference/is_plugin_active_for_network * @since 2.0 */ - if (!function_exists('is_plugin_active_for_network')) { + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { require_once ABSPATH . '/wp-admin/includes/plugin.php'; } // If is not activated on network, it will not have site options - if (!is_plugin_active_for_network(Core::PLUGIN_FILE)) { - if ((int) $this->conf(self::O_CACHE) == self::VAL_ON2) { + if ( ! is_plugin_active_for_network( Core::PLUGIN_FILE ) ) { + if ( self::VAL_ON2 === (int) $this->conf( self::O_CACHE ) ) { // Default to cache on - $this->set_conf(self::_CACHE, true); + $this->set_conf( self::_CACHE, true ); } return false; } @@ -280,19 +305,20 @@ private function _if_need_site_options() { * * @since 3.0 * @access private + * @return void */ private function _conf_site_db_init() { $this->load_site_options(); - $ver = $this->network_conf(self::_VER); + $ver = $this->network_conf( self::_VER ); /** * Don't upgrade or run new installations other than from backend visit * In this case, just use default conf */ - if (!$ver || $ver != Core::VER) { - if (!is_admin() && !defined('LITESPEED_CLI')) { - $this->set_network_conf($this->load_default_site_vals()); + if ( ! $ver || Core::VER !== $ver ) { + if ( ! is_admin() && ! defined( 'LITESPEED_CLI' ) ) { + $this->set_network_conf( $this->load_default_site_vals() ); return; } } @@ -300,22 +326,22 @@ private function _conf_site_db_init() { /** * Upgrade conf */ - if ($ver && $ver != Core::VER) { + if ( $ver && Core::VER !== $ver ) { // Site plugin version will change inside - Data::cls()->conf_site_upgrade($ver); + Data::cls()->conf_site_upgrade( $ver ); } /** * Is a new installation */ - if (!$ver || $ver != Core::VER) { + if ( ! $ver || Core::VER !== $ver ) { // Load default values $this->load_default_site_vals(); // Init new default/missing options - foreach (self::$_default_site_options as $k => $v) { + foreach ( self::$_default_site_options as $k => $v ) { // If the option existed, bypass updating - self::add_site_option($k, $v); + self::add_site_option( $k, $v ); } } } @@ -327,17 +353,18 @@ private function _conf_site_db_init() { * * @since 1.0.2 * @access public + * @return null|void */ public function load_site_options() { - if (!is_multisite()) { + if ( ! is_multisite() ) { return null; } // Load all site options - foreach (self::$_default_site_options as $k => $v) { - $val = self::get_site_option($k, $v); - $val = $this->type_casting($val, $k, true); - $this->set_network_conf($k, $val); + foreach ( self::$_default_site_options as $k => $v ) { + $val = self::get_site_option( $k, $v ); + $val = $this->type_casting( $val, $k, true ); + $this->set_network_conf( $k, $val ); } } @@ -350,11 +377,15 @@ public function load_site_options() { * * @since 3.0 * @access public + * + * @param string $name Option name. + * @param mixed $default_val Default value. + * @return void */ - public function option_append( $name, $default ) { - self::$_default_options[$name] = $default; - $this->set_conf($name, self::get_option($name, $default)); - $this->set_conf($name, $this->type_casting($this->conf($name), $name)); + public function option_append( $name, $default_val ) { + self::$_default_options[ $name ] = $default_val; + $this->set_conf( $name, self::get_option( $name, $default_val ) ); + $this->set_conf( $name, $this->type_casting( $this->conf( $name ), $name ) ); } /** @@ -362,21 +393,26 @@ public function option_append( $name, $default ) { * * @since 2.6 * @access public + * + * @param string $k Option key. + * @param mixed $v Option value. + * @return void */ public function force_option( $k, $v ) { - if (!$this->has_conf($k)) { + if ( ! $this->has_conf( $k ) ) { return; } - $v = $this->type_casting($v, $k); + $v = $this->type_casting( $v, $k ); - if ($this->conf($k) === $v) { + if ( $this->conf( $k ) === $v ) { return; } - Debug2::debug("[Conf] ** $k forced from " . var_export($this->conf($k), true) . ' to ' . var_export($v, true)); + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export + Debug2::debug( '[Conf] ** ' . $k . ' forced from ' . var_export( $this->conf( $k ), true ) . ' to ' . var_export( $v, true ) ); - $this->set_conf($k, $v); + $this->set_conf( $k, $v ); } /** @@ -384,24 +420,25 @@ public function force_option( $k, $v ) { * * @since 3.0 * @access public + * @return void */ public function define_cache() { // Init global const cache on setting - $this->set_conf(self::_CACHE, false); - if ((int) $this->conf(self::O_CACHE) == self::VAL_ON || $this->conf(self::O_CDN_QUIC)) { - $this->set_conf(self::_CACHE, true); + $this->set_conf( self::_CACHE, false ); + if ( self::VAL_ON === (int) $this->conf( self::O_CACHE ) || $this->conf( self::O_CDN_QUIC ) ) { + $this->set_conf( self::_CACHE, true ); } // Check network - if (!$this->_if_need_site_options()) { + if ( ! $this->_if_need_site_options() ) { // Set cache on $this->_define_cache_on(); return; } // If use network setting - if ((int) $this->conf(self::O_CACHE) == self::VAL_ON2 && $this->network_conf(self::O_CACHE)) { - $this->set_conf(self::_CACHE, true); + if ( self::VAL_ON2 === (int) $this->conf( self::O_CACHE ) && $this->network_conf( self::O_CACHE ) ) { + $this->set_conf( self::_CACHE, true ); } $this->_define_cache_on(); @@ -412,25 +449,16 @@ public function define_cache() { * * @since 2.1 * @access private + * @return void */ private function _define_cache_on() { - if (!$this->conf(self::_CACHE)) { + if ( ! $this->conf( self::_CACHE ) ) { return; } - defined('LITESPEED_ALLOWED') && !defined('LITESPEED_ON') && define('LITESPEED_ON', true); - } - - /** - * Get an option value - * - * @since 3.0 - * @access public - * @deprecated 4.0 Use $this->conf() instead - */ - public static function val( $id, $ori = false ) { - error_log('Called deprecated function \LiteSpeed\Conf::val(). Please use API call instead.'); - return self::cls()->conf($id, $ori); + if ( defined( 'LITESPEED_ALLOWED' ) && ! defined( 'LITESPEED_ON' ) ) { + define( 'LITESPEED_ON', true ); + } } /** @@ -438,57 +466,61 @@ public static function val( $id, $ori = false ) { * * @since 3.0 * @access public + * + * @param array<string,mixed> $the_matrix Option-value map. + * @return void */ - public function update_confs( $the_matrix = false ) { - if ($the_matrix) { - foreach ($the_matrix as $id => $val) { - $this->update($id, $val); + public function update_confs( $the_matrix = [] ) { + if ( $the_matrix ) { + foreach ( $the_matrix as $id => $val ) { + $this->update( $id, $val ); } } - if ($this->_updated_ids) { - foreach ($this->_updated_ids as $id) { + if ( $this->_updated_ids ) { + foreach ( $this->_updated_ids as $id ) { // Check if need to do a purge all or not - if ($this->_conf_purge_all($id)) { - Purge::purge_all('conf changed [id] ' . $id); + if ( $this->_conf_purge_all( $id ) ) { + Purge::purge_all( 'conf changed [id] ' . $id ); } // Check if need to purge a tag - if ($tag = $this->_conf_purge_tag($id)) { - Purge::add($tag); + $tag = $this->_conf_purge_tag( $id ); + if ( $tag ) { + Purge::add( $tag ); } // Update cron - if ($this->_conf_cron($id)) { - $this->cls('Task')->try_clean($id); + if ( $this->_conf_cron( $id ) ) { + $this->cls( 'Task' )->try_clean( $id ); } // Reset crawler bypassed list when any of the options WebP replace, guest mode, or cache mobile got changed - if ($id == self::O_IMG_OPTM_WEBP || $id == self::O_GUEST || $id == self::O_CACHE_MOBILE) { - $this->cls('Crawler')->clear_disabled_list(); + if ( self::O_IMG_OPTM_WEBP === $id || self::O_GUEST === $id || self::O_CACHE_MOBILE === $id ) { + $this->cls( 'Crawler' )->clear_disabled_list(); } } } - do_action('litespeed_update_confs', $the_matrix); + do_action( 'litespeed_update_confs', $the_matrix ); // Update related tables - $this->cls('Data')->correct_tb_existence(); + $this->cls( 'Data' )->correct_tb_existence(); // Update related files - $this->cls('Activation')->update_files(); + $this->cls( 'Activation' )->update_files(); /** * CDN related actions - Cloudflare */ - $this->cls('CDN\Cloudflare')->try_refresh_zone(); + $this->cls( 'CDN\Cloudflare' )->try_refresh_zone(); /** * CDN related actions - QUIC.cloud * * @since 2.3 */ - $this->cls('CDN\Quic')->try_sync_conf(); + $this->cls( 'CDN\Quic' )->try_sync_conf(); } /** @@ -498,6 +530,10 @@ public function update_confs( $the_matrix = false ) { * * @since 3.0 * @access public + * + * @param string $id Option ID. + * @param mixed $val Option value. + * @return void */ public function update( $id, $val ) { // Bypassed this bcos $this->_options could be changed by force_option() @@ -505,62 +541,66 @@ public function update( $id, $val ) { // return; // } - if ($id == self::_VER) { + if ( self::_VER === $id ) { return; } - if ($id == self::O_SERVER_IP) { - if ($val && !Utility::valid_ipv4($val)) { - $msg = sprintf(__('Saving option failed. IPv4 only for %s.', 'litespeed-cache'), Lang::title(Base::O_SERVER_IP)); - Admin_Display::error($msg); + if ( self::O_SERVER_IP === $id ) { + if ( $val && ! Utility::valid_ipv4( $val ) ) { + $msg = sprintf( __( 'Saving option failed. IPv4 only for %s.', 'litespeed-cache' ), Lang::title( Base::O_SERVER_IP ) ); + Admin_Display::error( $msg ); return; } } - if (!array_key_exists($id, self::$_default_options)) { - defined('LSCWP_LOG') && Debug2::debug('[Conf] Invalid option ID ' . $id); + if ( ! array_key_exists( $id, self::$_default_options ) ) { + if ( defined( 'LSCWP_LOG' ) ) { + Debug2::debug( '[Conf] Invalid option ID ' . $id ); + } return; } - if ($val && $this->_conf_pswd($id) && !preg_match('/[^\*]/', $val)) { + if ( $val && $this->_conf_pswd( $id ) && ! preg_match( '/[^\*]/', (string) $val ) ) { return; } // Special handler for CDN Original URLs - if ($id == self::O_CDN_ORI && !$val) { - $site_url = site_url('/'); - $parsed = parse_url($site_url); - $site_url = str_replace($parsed['scheme'] . ':', '', $site_url); + if ( self::O_CDN_ORI === $id && ! $val ) { + $site_url = site_url( '/' ); + $parsed = wp_parse_url( $site_url ); + if ( !empty( $parsed['scheme'] ) ) { + $site_url = str_replace( $parsed['scheme'] . ':', '', $site_url ); + } $val = $site_url; } // Validate type - $val = $this->type_casting($val, $id); + $val = $this->type_casting( $val, $id ); // Save data - self::update_option($id, $val); + self::update_option( $id, $val ); // Handle purge if setting changed - if ($this->conf($id) != $val) { + if ( $this->conf( $id ) !== $val ) { $this->_updated_ids[] = $id; // Check if need to fire a purge or not (Here has to stay inside `update()` bcos need comparing old value) - if ($this->_conf_purge($id)) { - $diff = array_diff($val, $this->conf($id)); - $diff2 = array_diff($this->conf($id), $val); - $diff = array_merge($diff, $diff2); + if ( $this->_conf_purge( $id ) ) { + $old = (array) $this->conf( $id ); + $new = (array) $val; + $diff = array_merge( array_diff( $new, $old ), array_diff( $old, $new ) ); // If has difference - foreach ($diff as $v) { - $v = ltrim($v, '^'); - $v = rtrim($v, '$'); - $this->cls('Purge')->purge_url($v); + foreach ( $diff as $v ) { + $v = ltrim( (string) $v, '^' ); + $v = rtrim( (string) $v, '$' ); + $this->cls( 'Purge' )->purge_url( $v ); } } } // Update in-memory data - $this->set_conf($id, $val); + $this->set_conf( $id, $val ); } /** @@ -568,55 +608,61 @@ public function update( $id, $val ) { * * @since 3.0 * @access public + * + * @param string $id Option ID. + * @param mixed $val Option value. + * @return void */ public function network_update( $id, $val ) { - if (!array_key_exists($id, self::$_default_site_options)) { - defined('LSCWP_LOG') && Debug2::debug('[Conf] Invalid network option ID ' . $id); + if ( ! array_key_exists( $id, self::$_default_site_options ) ) { + if ( defined( 'LSCWP_LOG' ) ) { + Debug2::debug( '[Conf] Invalid network option ID ' . $id ); + } return; } - if ($val && $this->_conf_pswd($id) && !preg_match('/[^\*]/', $val)) { + if ( $val && $this->_conf_pswd( $id ) && ! preg_match( '/[^\*]/', (string) $val ) ) { return; } // Validate type - if (is_bool(self::$_default_site_options[$id])) { - $max = $this->_conf_multi_switch($id); - if ($max && $val > 1) { - $val %= $max + 1; + if ( is_bool( self::$_default_site_options[ $id ] ) ) { + $max = $this->_conf_multi_switch( $id ); + if ( $max && $val > 1 ) { + $val %= ( $max + 1 ); } else { $val = (bool) $val; } - } elseif (is_array(self::$_default_site_options[$id])) { + } elseif ( is_array( self::$_default_site_options[ $id ] ) ) { // from textarea input - if (!is_array($val)) { - $val = Utility::sanitize_lines($val, $this->_conf_filter($id)); + if ( ! is_array( $val ) ) { + $val = Utility::sanitize_lines( $val, $this->_conf_filter( $id ) ); } - } elseif (!is_string(self::$_default_site_options[$id])) { + } elseif ( ! is_string( self::$_default_site_options[ $id ] ) ) { $val = (int) $val; } else { // Check if the string has a limit set - $val = $this->_conf_string_val($id, $val); + $val = $this->_conf_string_val( $id, $val ); } // Save data - self::update_site_option($id, $val); + self::update_site_option( $id, $val ); // Handle purge if setting changed - if ($this->network_conf($id) != $val) { + if ( $this->network_conf( $id ) !== $val ) { // Check if need to do a purge all or not - if ($this->_conf_purge_all($id)) { - Purge::purge_all('[Conf] Network conf changed [id] ' . $id); + if ( $this->_conf_purge_all( $id ) ) { + Purge::purge_all( '[Conf] Network conf changed [id] ' . $id ); } // Update in-memory data - $this->set_network_conf($id, $val); + $this->set_network_conf( $id, $val ); } // No need to update cron here, Cron will register in each init - if ($this->has_conf($id)) { - $this->set_conf($id, $val); + if ( $this->has_conf( $id ) ) { + $this->set_conf( $id, $val ); } } @@ -625,23 +671,24 @@ public function network_update( $id, $val ) { * * @since 1.6 * @access public - * @param string $role The user role - * @return int The set value if already set + * + * @param string|null $role The user role. + * @return string|false The set value if already set, otherwise false. */ public function in_optm_exc_roles( $role = null ) { // Get user role - if ($role === null) { + if ( null === $role ) { $role = Router::get_role(); } - if (!$role) { + if ( ! $role ) { return false; } - $roles = explode(',', $role); - $found = array_intersect($roles, $this->conf(self::O_OPTM_EXC_ROLES)); + $roles = explode( ',', $role ); + $found = array_intersect( $roles, $this->conf( self::O_OPTM_EXC_ROLES ) ); - return $found ? implode(',', $found) : false; + return $found ? implode( ',', $found ) : false; } /** @@ -649,6 +696,7 @@ public function in_optm_exc_roles( $role = null ) { * * @since 2.9 * @access private + * @return void */ private function _set_conf() { /** @@ -657,41 +705,48 @@ private function _set_conf() { * 2. If replace the array setting with one line, use `set[cache-force_uri]=the_url`. * 3. If replace the array setting with multi lines value, use 2 then 1. */ - if (empty($_GET[self::TYPE_SET]) || !is_array($_GET[self::TYPE_SET])) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput + $raw = !empty( $_GET[ self::TYPE_SET ] ) ? $_GET[ self::TYPE_SET ] : false; + if ( !$raw || ! is_array( $raw ) ) { return; } - $the_matrix = array(); - foreach ($_GET[self::TYPE_SET] as $id => $v) { - if (!$this->has_conf($id)) { + // Sanitize the incoming matrix. + $the_matrix = []; + foreach ( $raw as $id => $v ) { + if ( ! $this->has_conf( $id ) ) { continue; } // Append new item to array type settings - if (is_array($v) && is_array($this->conf($id))) { - $v = array_merge($this->conf($id), $v); + if ( is_array( $v ) && is_array( $this->conf( $id ) ) ) { + $v = array_merge( $this->conf( $id ), $v ); - Debug2::debug('[Conf] Appended to settings [' . $id . ']: ' . var_export($v, true)); + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export + Debug2::debug( '[Conf] Appended to settings [' . $id . ']: ' . var_export( $v, true ) ); } else { - Debug2::debug('[Conf] Set setting [' . $id . ']: ' . var_export($v, true)); + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export + Debug2::debug( '[Conf] Set setting [' . $id . ']: ' . var_export( $v, true ) ); } - $the_matrix[$id] = $v; + $the_matrix[ $id ] = $v; } - if (!$the_matrix) { + if ( !$the_matrix ) { return; } - $this->update_confs($the_matrix); + $this->update_confs( $the_matrix ); - $msg = __('Changed setting successfully.', 'litespeed-cache'); - Admin_Display::success($msg); + $msg = __( 'Changed setting successfully.', 'litespeed-cache' ); + Admin_Display::success( $msg ); // Redirect if changed frontend URL - if (!empty($_GET['redirect'])) { - wp_redirect($_GET['redirect']); - exit(); + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $redirect = ! empty( $_GET['redirect'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect'] ) ) : ''; + if ( $redirect ) { + wp_safe_redirect( $redirect ); + exit; } } @@ -700,13 +755,14 @@ private function _set_conf() { * * @since 2.9 * @access public + * @return void */ public function handler() { $type = Router::verify_type(); - switch ($type) { + switch ( $type ) { case self::TYPE_SET: - $this->_set_conf(); + $this->_set_conf(); break; default: diff --git a/src/control.cls.php b/src/control.cls.php index d9eb38094..aea0f82ea 100644 --- a/src/control.cls.php +++ b/src/control.cls.php @@ -1,18 +1,24 @@ <?php - /** - * The plugin cache-control class for X-Litespeed-Cache-Control + * The plugin cache-control class for X-LiteSpeed-Cache-Control. + * + * Provides helpers for determining cacheability, emitting cache-control headers, + * and honoring various LiteSpeed Cache configuration options. * - * @since 1.1.3 * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> + * @since 1.1.3 */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Control + * + * Handles cache-control flags, TTL calculation, redirection checks, + * role-based exclusions, and final header output. + */ class Control extends Root { const LOG_TAG = '💵'; @@ -28,50 +34,70 @@ class Control extends Root { const X_HEADER = 'X-LiteSpeed-Cache-Control'; - protected static $_control = 0; + /** + * Bitmask control flags for current request. + * + * @var int + */ + protected static $_control = 0; + + /** + * Custom TTL for current request (seconds). + * + * @var int + */ protected static $_custom_ttl = 0; - private $_response_header_ttls = array(); + /** + * Mapping of HTTP status codes to custom TTLs. + * + * @var array<string,int|string> + */ + private $_response_header_ttls = []; /** - * Init cache control + * Init cache control. * * @since 1.6.2 + * @return void */ public function init() { /** - * Add vary filter for Role Excludes + * Add vary filter for Role Excludes. * * @since 1.6.2 */ - add_filter('litespeed_vary', array( $this, 'vary_add_role_exclude' )); + add_filter( 'litespeed_vary', [ $this, 'vary_add_role_exclude' ] ); - // 301 redirect hook - add_filter('wp_redirect', array( $this, 'check_redirect' ), 10, 2); + // 301 redirect hook. + add_filter( 'wp_redirect', [ $this, 'check_redirect' ], 10, 2 ); - // Load response header conf - $this->_response_header_ttls = $this->conf(Base::O_CACHE_TTL_STATUS); - foreach ($this->_response_header_ttls as $k => $v) { - $v = explode(' ', $v); - if (empty($v[0]) || empty($v[1])) { + // Load response header conf. + $this->_response_header_ttls = $this->conf( Base::O_CACHE_TTL_STATUS ); + foreach ( $this->_response_header_ttls as $k => $v ) { + $v = explode( ' ', $v ); + if ( empty( $v[0] ) || empty( $v[1] ) ) { continue; } - $this->_response_header_ttls[$v[0]] = $v[1]; + $this->_response_header_ttls[ $v[0] ] = $v[1]; } - if ($this->conf(Base::O_PURGE_STALE)) { + if ( $this->conf( Base::O_PURGE_STALE ) ) { $this->set_stale(); } } /** - * Exclude role from optimization filter + * Exclude role from optimization filter. * * @since 1.6.2 * @access public + * + * @param array<string,mixed> $vary Existing vary map. + * @return array<string,mixed> */ public function vary_add_role_exclude( $vary ) { - if ($this->in_cache_exc_roles()) { + if ( $this->in_cache_exc_roles() ) { $vary['role_exclude_cache'] = 1; } @@ -79,28 +105,29 @@ public function vary_add_role_exclude( $vary ) { } /** - * Check if one user role is in exclude cache group settings + * Check if one user role is in exclude cache group settings. * * @since 1.6.2 * @since 3.0 Moved here from conf.cls * @access public - * @param string $role The user role - * @return int The set value if already set + * + * @param string|null $role The user role. + * @return string|false Comma-separated roles if set, otherwise false. */ public function in_cache_exc_roles( $role = null ) { - // Get user role - if ($role === null) { + // Get user role. + if ( null === $role ) { $role = Router::get_role(); } - if (!$role) { + if ( ! $role ) { return false; } - $roles = explode(',', $role); - $found = array_intersect($roles, $this->conf(Base::O_CACHE_EXC_ROLES)); + $roles = explode( ',', $role ); + $found = array_intersect( $roles, $this->conf( Base::O_CACHE_EXC_ROLES ) ); - return $found ? implode(',', $found) : false; + return $found ? implode( ',', $found ) : false; } /** @@ -109,37 +136,37 @@ public function in_cache_exc_roles( $role = null ) { * * @since 1.1.3 * @access public + * @return void */ public function init_cacheable() { - // Hook `wp` to mark default cacheable status - // NOTE: Any process that does NOT run into `wp` hook will not get cacheable by default - add_action('wp', array( $this, 'set_cacheable' ), 5); + // Hook `wp` to mark default cacheable status. + // NOTE: Any process that does NOT run into `wp` hook will not get cacheable by default. + add_action( 'wp', [ $this, 'set_cacheable' ], 5 ); - // Hook WP REST to be cacheable - if ($this->conf(Base::O_CACHE_REST)) { - add_action('rest_api_init', array( $this, 'set_cacheable' ), 5); + // Hook WP REST to be cacheable. + if ( $this->conf( Base::O_CACHE_REST ) ) { + add_action( 'rest_api_init', [ $this, 'set_cacheable' ], 5 ); } - // AJAX cache - $ajax_cache = $this->conf(Base::O_CACHE_AJAX_TTL); - foreach ($ajax_cache as $v) { - $v = explode(' ', $v); - if (empty($v[0]) || empty($v[1])) { + // AJAX cache. + $ajax_cache = $this->conf( Base::O_CACHE_AJAX_TTL ); + foreach ( $ajax_cache as $v ) { + $v = explode( ' ', $v ); + if ( empty( $v[0] ) || empty( $v[1] ) ) { continue; } - // self::debug("Initializing cacheable status for wp_ajax_nopriv_" . $v[0]); add_action( 'wp_ajax_nopriv_' . $v[0], function () use ( $v ) { - self::set_custom_ttl($v[1]); - self::force_cacheable('ajax Cache setting for action ' . $v[0]); + self::set_custom_ttl( $v[1] ); + self::force_cacheable( 'ajax Cache setting for action ' . $v[0] ); }, 4 ); } - // Check error page - add_filter('status_header', array( $this, 'check_error_codes' ), 10, 2); + // Check error page. + add_filter( 'status_header', [ $this, 'check_error_codes' ], 10, 2 ); } /** @@ -147,244 +174,268 @@ function () use ( $v ) { * * @since 1.0.13.1 * @access public - * @param $status_header - * @param $code - * @return $error_status + * + * @param string $status_header Status header. + * @param int $code HTTP status code. + * @return string Original status header. */ public function check_error_codes( $status_header, $code ) { - if (array_key_exists($code, $this->_response_header_ttls)) { - if (self::is_cacheable() && !$this->_response_header_ttls[$code]) { - self::set_nocache('[Ctrl] TTL is set to no cache [status_header] ' . $code); + if ( array_key_exists( $code, $this->_response_header_ttls ) ) { + if ( self::is_cacheable() && ! $this->_response_header_ttls[ $code ] ) { + self::set_nocache( '[Ctrl] TTL is set to no cache [status_header] ' . $code ); } - // Set TTL - self::set_custom_ttl($this->_response_header_ttls[$code]); - } elseif (self::is_cacheable()) { - if (substr($code, 0, 1) == 4 || substr($code, 0, 1) == 5) { - self::set_nocache('[Ctrl] 4xx/5xx default to no cache [status_header] ' . $code); + // Set TTL. + self::set_custom_ttl( $this->_response_header_ttls[ $code ] ); + } elseif ( self::is_cacheable() ) { + $first = substr( $code, 0, 1 ); + if ( '4' === $first || '5' === $first ) { + self::set_nocache( '[Ctrl] 4xx/5xx default to no cache [status_header] ' . $code ); } } - // Set cache tag - if (in_array($code, Tag::$error_code_tags)) { - Tag::add(Tag::TYPE_HTTP . $code); + // Set cache tag. + if ( in_array( $code, Tag::$error_code_tags, true ) ) { + Tag::add( Tag::TYPE_HTTP . $code ); } - // Give the default status_header back + // Give the default status_header back. return $status_header; } /** - * Set no vary setting + * Set no vary setting. * * @access public * @since 1.1.3 + * @return void */ public static function set_no_vary() { - if (self::is_no_vary()) { + if ( self::is_no_vary() ) { return; } self::$_control |= self::BM_NO_VARY; - self::debug('X Cache_control -> no-vary', 3); + self::debug( 'X Cache_control -> no-vary', 3 ); } /** - * Get no vary setting + * Get no vary setting. * * @access public * @since 1.1.3 + * @return bool */ public static function is_no_vary() { return self::$_control & self::BM_NO_VARY; } /** - * Set stale + * Set stale. * * @access public * @since 1.1.3 + * @return void */ public function set_stale() { - if (self::is_stale()) { + if ( self::is_stale() ) { return; } self::$_control |= self::BM_STALE; - self::debug('X Cache_control -> stale'); + self::debug( 'X Cache_control -> stale' ); } /** - * Get stale + * Get stale. * * @access public * @since 1.1.3 + * @return bool */ public static function is_stale() { return self::$_control & self::BM_STALE; } /** - * Set cache control to shared private + * Set cache control to shared private. * * @access public * @since 1.1.3 - * @param string $reason The reason to no cache + * + * @param string|false $reason The reason to mark shared, or false. + * @return void */ public static function set_shared( $reason = false ) { - if (self::is_shared()) { + if ( self::is_shared() ) { return; } self::$_control |= self::BM_SHARED; self::set_private(); - if (!is_string($reason)) { + if ( ! is_string( $reason ) ) { $reason = false; } - if ($reason) { + if ( $reason ) { $reason = "( $reason )"; } - self::debug('X Cache_control -> shared ' . $reason); + self::debug( 'X Cache_control -> shared ' . $reason ); } /** - * Check if is shared private + * Check if is shared private. * * @access public * @since 1.1.3 + * @return bool */ public static function is_shared() { - return self::$_control & self::BM_SHARED && self::is_private(); + return (bool) ( self::$_control & self::BM_SHARED ) && self::is_private(); } /** - * Set cache control to forced public + * Set cache control to forced public. * * @access public * @since 1.7.1 + * + * @param string|false $reason Reason text or false. + * @return void */ public static function set_public_forced( $reason = false ) { - if (self::is_public_forced()) { + if ( self::is_public_forced() ) { return; } self::$_control |= self::BM_PUBLIC_FORCED; - if (!is_string($reason)) { + if ( ! is_string( $reason ) ) { $reason = false; } - if ($reason) { + if ( $reason ) { $reason = "( $reason )"; } - self::debug('X Cache_control -> public forced ' . $reason); + self::debug( 'X Cache_control -> public forced ' . $reason ); } /** - * Check if is public forced + * Check if is public forced. * * @access public * @since 1.7.1 + * @return bool */ public static function is_public_forced() { return self::$_control & self::BM_PUBLIC_FORCED; } /** - * Set cache control to private + * Set cache control to private. * * @access public * @since 1.1.3 - * @param string $reason The reason to no cache + * + * @param string|false $reason The reason to set private. + * @return void */ public static function set_private( $reason = false ) { - if (self::is_private()) { + if ( self::is_private() ) { return; } self::$_control |= self::BM_PRIVATE; - if (!is_string($reason)) { + if ( ! is_string( $reason ) ) { $reason = false; } - if ($reason) { + if ( $reason ) { $reason = "( $reason )"; } - self::debug('X Cache_control -> private ' . $reason); + self::debug( 'X Cache_control -> private ' . $reason ); } /** - * Check if is private + * Check if is private. * * @access public * @since 1.1.3 + * @return bool */ public static function is_private() { - if (defined('LITESPEED_GUEST') && LITESPEED_GUEST) { + // if ( defined( 'LITESPEED_GUEST' ) && LITESPEED_GUEST ) { // return false; - } + // } - return self::$_control & self::BM_PRIVATE && !self::is_public_forced(); + return (bool) ( self::$_control & self::BM_PRIVATE ) && ! self::is_public_forced(); } /** - * Initialize cacheable status in `wp` hook, if not call this, by default it will be non-cacheable + * Initialize cacheable status in `wp` hook, if not call this, by default it will be non-cacheable. * * @access public * @since 1.1.3 + * + * @param string|false $reason Reason text or false. + * @return void */ public function set_cacheable( $reason = false ) { self::$_control |= self::BM_CACHEABLE; - if (!is_string($reason)) { + if ( ! is_string( $reason ) ) { $reason = false; } - if ($reason) { + if ( $reason ) { $reason = ' [reason] ' . $reason; } - self::debug('Cache_control init on' . $reason); + self::debug( 'Cache_control init on' . $reason ); } /** - * This will disable non-cacheable BM + * This will disable non-cacheable BM. * * @access public * @since 2.2 + * + * @param string|false $reason Reason text or false. + * @return void */ public static function force_cacheable( $reason = false ) { self::$_control |= self::BM_FORCED_CACHEABLE; - if (!is_string($reason)) { + if ( ! is_string( $reason ) ) { $reason = false; } - if ($reason) { + if ( $reason ) { $reason = ' [reason] ' . $reason; } - self::debug('Forced cacheable' . $reason); + self::debug( 'Forced cacheable' . $reason ); } /** - * Switch to nocacheable status + * Switch to nocacheable status. * * @access public * @since 1.1.3 - * @param string $reason The reason to no cache + * + * @param string|false $reason The reason to no cache. + * @return void */ public static function set_nocache( $reason = false ) { self::$_control |= self::BM_NOTCACHEABLE; - if (!is_string($reason)) { + if ( ! is_string( $reason ) ) { $reason = false; } - if ($reason) { + if ( $reason ) { $reason = "( $reason )"; } - self::debug('X Cache_control -> no Cache ' . $reason, 5); + self::debug( 'X Cache_control -> no Cache ' . $reason, 5 ); } /** - * Check current notcacheable bit set + * Check current notcacheable bit set. * * @access public * @since 1.1.3 @@ -395,44 +446,45 @@ public static function isset_notcacheable() { } /** - * Check current force cacheable bit set + * Check current force cacheable bit set. * * @access public * @since 2.2 + * @return bool */ public static function is_forced_cacheable() { return self::$_control & self::BM_FORCED_CACHEABLE; } /** - * Check current cacheable status + * Check current cacheable status. * * @access public * @since 1.1.3 * @return bool True if is still cacheable, otherwise false. */ public static function is_cacheable() { - if (defined('LSCACHE_NO_CACHE') && LSCACHE_NO_CACHE) { - self::debug('LSCACHE_NO_CACHE constant defined'); + if ( defined( 'LSCACHE_NO_CACHE' ) && LSCACHE_NO_CACHE ) { + self::debug( 'LSCACHE_NO_CACHE constant defined' ); return false; } // Guest mode always cacheable - if (defined('LITESPEED_GUEST') && LITESPEED_GUEST) { + // if ( defined( 'LITESPEED_GUEST' ) && LITESPEED_GUEST ) { // return true; - } + // } - // If its forced public cacheable - if (self::is_public_forced()) { + // If it's forced public cacheable. + if ( self::is_public_forced() ) { return true; } - // If its forced cacheable - if (self::is_forced_cacheable()) { + // If it's forced cacheable. + if ( self::is_forced_cacheable() ) { return true; } - return !self::isset_notcacheable() && self::$_control & self::BM_CACHEABLE; + return ! self::isset_notcacheable() && ( self::$_control & self::BM_CACHEABLE ); } /** @@ -440,12 +492,15 @@ public static function is_cacheable() { * * @access public * @since 1.1.3 - * @param mixed $ttl An integer or string to use as the TTL. Must be numeric. + * + * @param int|string $ttl An integer or numeric string to use as the TTL. + * @param string|false $reason Optional reason text. + * @return void */ public static function set_custom_ttl( $ttl, $reason = false ) { - if (is_numeric($ttl)) { - self::$_custom_ttl = $ttl; - self::debug('X Cache_control TTL -> ' . $ttl . ($reason ? ' [reason] ' . $ttl : '')); + if ( is_numeric( $ttl ) ) { + self::$_custom_ttl = (int) $ttl; + self::debug( 'X Cache_control TTL -> ' . $ttl . ( $reason ? ' [reason] ' . $ttl : '' ) ); } } @@ -454,97 +509,110 @@ public static function set_custom_ttl( $ttl, $reason = false ) { * * @access public * @since 1.1.3 + * @return int */ public function get_ttl() { - if (self::$_custom_ttl != 0) { - return self::$_custom_ttl; - } - - // Check if is in timed url list or not - $timed_urls = Utility::wildcard2regex($this->conf(Base::O_PURGE_TIMED_URLS)); - $timed_urls_time = $this->conf(Base::O_PURGE_TIMED_URLS_TIME); - if ($timed_urls && $timed_urls_time) { - $current_url = Tag::build_uri_tag(true); - // Use time limit ttl - $scheduled_time = strtotime($timed_urls_time); - $ttl = $scheduled_time - time(); - if ($ttl < 0) { + if ( 0 !== self::$_custom_ttl ) { + return (int) self::$_custom_ttl; + } + + // Check if is in timed url list or not. + $timed_urls = Utility::wildcard2regex( $this->conf( Base::O_PURGE_TIMED_URLS ) ); + $timed_urls_time = $this->conf( Base::O_PURGE_TIMED_URLS_TIME ); + if ( $timed_urls && $timed_urls_time ) { + $current_url = Tag::build_uri_tag( true ); + // Use time limit ttl. + $scheduled_time = strtotime( $timed_urls_time ); + $ttl = $scheduled_time - current_time('timestamp'); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp + if ( $ttl < 0 ) { $ttl += 86400; // add one day } - foreach ($timed_urls as $v) { - if (strpos($v, '*') !== false) { - if (preg_match('#' . $v . '#iU', $current_url)) { - self::debug('X Cache_control TTL is limited to ' . $ttl . ' due to scheduled purge regex ' . $v); + foreach ( $timed_urls as $v ) { + if ( false !== strpos( $v, '*' ) ) { + if ( preg_match( '#' . $v . '#iU', $current_url ) ) { + self::debug( 'X Cache_control TTL is limited to ' . $ttl . ' due to scheduled purge regex ' . $v ); return $ttl; } - } elseif ($v == $current_url) { - self::debug('X Cache_control TTL is limited to ' . $ttl . ' due to scheduled purge rule ' . $v); + } elseif ( $v === $current_url ) { + self::debug( 'X Cache_control TTL is limited to ' . $ttl . ' due to scheduled purge rule ' . $v ); return $ttl; } } } - // Private cache uses private ttl setting - if (self::is_private()) { - return $this->conf(Base::O_CACHE_TTL_PRIV); + // Private cache uses private ttl setting. + if ( self::is_private() ) { + return (int) $this->conf( Base::O_CACHE_TTL_PRIV ); } - if (is_front_page()) { - return $this->conf(Base::O_CACHE_TTL_FRONTPAGE); + if ( is_front_page() ) { + return (int) $this->conf( Base::O_CACHE_TTL_FRONTPAGE ); } - $feed_ttl = $this->conf(Base::O_CACHE_TTL_FEED); - if (is_feed() && $feed_ttl > 0) { + $feed_ttl = (int) $this->conf( Base::O_CACHE_TTL_FEED ); + if ( is_feed() && $feed_ttl > 0 ) { return $feed_ttl; } - if ($this->cls('REST')->is_rest() || $this->cls('REST')->is_internal_rest()) { - return $this->conf(Base::O_CACHE_TTL_REST); + if ( $this->cls( 'REST' )->is_rest() || $this->cls( 'REST' )->is_internal_rest() ) { + return (int) $this->conf( Base::O_CACHE_TTL_REST ); } - return $this->conf(Base::O_CACHE_TTL_PUB); + return (int) $this->conf( Base::O_CACHE_TTL_PUB ); } /** - * Check if need to set no cache status for redirection or not + * Check if need to set no cache status for redirection or not. * * @access public * @since 1.1.3 + * + * @param string $location Redirect location. + * @param int $status HTTP status. + * @return string Redirect location. */ public function check_redirect( $location, $status ) { - // TODO: some env don't have SCRIPT_URI but only REQUEST_URI, need to be compatible - if (!empty($_SERVER['SCRIPT_URI'])) { - // dont check $status == '301' anymore - self::debug('301 from ' . $_SERVER['SCRIPT_URI']); - self::debug("301 to $location"); + $script_uri = ''; + if ( !empty( $_SERVER['SCRIPT_URI'] ) ) { + $script_uri = sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_URI'] ) ); + } elseif ( !empty( $_SERVER['REQUEST_URI'] ) ) { + $home = trailingslashit( home_url() ); + $script_uri = $home . ltrim( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), '/' ); + } + + if ( '' !== $script_uri ) { + self::debug( '301 from ' . $script_uri ); + self::debug( '301 to ' . $location ); - $to_check = array( PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PATH, PHP_URL_QUERY ); + $to_check = [ PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PATH, PHP_URL_QUERY ]; $is_same_redirect = true; - foreach ($to_check as $v) { - $url_parsed = $v == PHP_URL_QUERY ? $_SERVER['QUERY_STRING'] : parse_url($_SERVER['SCRIPT_URI'], $v); - $target = parse_url($location, $v); + $query_string = ! empty( $_SERVER['QUERY_STRING'] ) ? sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) : ''; + foreach ( $to_check as $v ) { + $url_parsed = PHP_URL_QUERY === $v ? $query_string : wp_parse_url( $script_uri, $v ); - self::debug("Compare [from] $url_parsed [to] $target"); + $target = wp_parse_url( $location, $v ); - if ($v == PHP_URL_QUERY) { - $url_parsed = $url_parsed ? urldecode($url_parsed) : ''; - $target = $target ? urldecode($target) : ''; - if (substr($url_parsed, -1) == '&') { - $url_parsed = substr($url_parsed, 0, -1); + self::debug( 'Compare [from] ' . $url_parsed . ' [to] ' . $target ); + + if ( PHP_URL_QUERY === $v ) { + $url_parsed = $url_parsed ? urldecode( $url_parsed ) : ''; + $target = $target ? urldecode( $target ) : ''; + if ( '&' === substr( $url_parsed, -1 ) ) { + $url_parsed = substr( $url_parsed, 0, -1 ); } } - if ($url_parsed != $target) { + if ( $url_parsed !== $target ) { $is_same_redirect = false; - self::debug('301 different redirection'); + self::debug( '301 different redirection' ); break; } } - if ($is_same_redirect) { - self::set_nocache('301 to same url'); + if ( $is_same_redirect ) { + self::set_nocache( '301 to same url' ); } } @@ -560,14 +628,15 @@ public function check_redirect( $location, $status ) { */ public function output() { $esi_hdr = ''; - if (ESI::has_esi()) { + if ( ESI::has_esi() ) { $esi_hdr = ',esi=on'; } $hdr = self::X_HEADER . ': '; - if (defined('DONOTCACHEPAGE') && apply_filters('litespeed_const_DONOTCACHEPAGE', DONOTCACHEPAGE)) { - self::debug('❌ forced no cache [reason] DONOTCACHEPAGE const'); + // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase + if ( defined( 'DONOTCACHEPAGE' ) && apply_filters( 'litespeed_const_DONOTCACHEPAGE', DONOTCACHEPAGE ) ) { + self::debug( '❌ forced no cache [reason] DONOTCACHEPAGE const' ); $hdr .= 'no-cache' . $esi_hdr; return $hdr; } @@ -600,15 +669,15 @@ public function output() { return $hdr; } - if (self::is_shared()) { + if ( self::is_shared() ) { $hdr .= 'shared,private'; - } elseif (self::is_private()) { + } elseif ( self::is_private() ) { $hdr .= 'private'; } else { $hdr .= 'public'; } - if (self::is_no_vary()) { + if ( self::is_no_vary() ) { $hdr .= ',no-vary'; } @@ -617,133 +686,139 @@ public function output() { } /** - * Generate all `control` tags before output + * Generate all `control` tags before output. * * @access public * @since 1.1.3 + * @return void */ public function finalize() { - if (defined('LITESPEED_GUEST') && LITESPEED_GUEST) { + // if ( defined( 'LITESPEED_GUEST' ) && LITESPEED_GUEST ) { // return; - } + // } - if (is_preview()) { - self::set_nocache('preview page'); + if ( is_preview() ) { + self::set_nocache( 'preview page' ); return; } - // Check if has metabox non-cacheable setting or not - if (file_exists(LSCWP_DIR . 'src/metabox.cls.php') && $this->cls('Metabox')->setting('litespeed_no_cache')) { - self::set_nocache('per post metabox setting'); + // Check if has metabox non-cacheable setting or not. + if ( file_exists( LSCWP_DIR . 'src/metabox.cls.php' ) && $this->cls( 'Metabox' )->setting( 'litespeed_no_cache' ) ) { + self::set_nocache( 'per post metabox setting' ); return; } - // Check if URI is forced public cache - $excludes = $this->conf(Base::O_CACHE_FORCE_PUB_URI); - $hit = Utility::str_hit_array($_SERVER['REQUEST_URI'], $excludes, true); - if ($hit) { - list($result, $this_ttl) = $hit; - self::set_public_forced('Setting: ' . $result); - self::debug('Forced public cacheable due to setting: ' . $result); - if ($this_ttl) { - self::set_custom_ttl($this_ttl); + // Check if URI is forced public cache. + $excludes = $this->conf( Base::O_CACHE_FORCE_PUB_URI ); + $req_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + $hit = Utility::str_hit_array( $req_uri, $excludes, true ); + if ( $hit ) { + list( $result, $this_ttl ) = $hit; + self::set_public_forced( 'Setting: ' . $result ); + self::debug( 'Forced public cacheable due to setting: ' . $result ); + if ( $this_ttl ) { + self::set_custom_ttl( $this_ttl ); } } - if (self::is_public_forced()) { + if ( self::is_public_forced() ) { return; } - // Check if URI is forced cache - $excludes = $this->conf(Base::O_CACHE_FORCE_URI); - $hit = Utility::str_hit_array($_SERVER['REQUEST_URI'], $excludes, true); - if ($hit) { - list($result, $this_ttl) = $hit; + // Check if URI is forced cache. + $excludes = $this->conf( Base::O_CACHE_FORCE_URI ); + $hit = Utility::str_hit_array( $req_uri, $excludes, true ); + if ( $hit ) { + list( $result, $this_ttl ) = $hit; self::force_cacheable(); - self::debug('Forced cacheable due to setting: ' . $result); - if ($this_ttl) { - self::set_custom_ttl($this_ttl); + self::debug( 'Forced cacheable due to setting: ' . $result ); + if ( $this_ttl ) { + self::set_custom_ttl( $this_ttl ); } } - // if is not cacheable, terminate check - // Even no need to run 3rd party hook - if (!self::is_cacheable()) { - self::debug('not cacheable before ctrl finalize'); + // if is not cacheable, terminate check. + // Even no need to run 3rd party hook. + if ( ! self::is_cacheable() ) { + self::debug( 'not cacheable before ctrl finalize' ); return; } - // Apply 3rd party filter - // NOTE: Hook always needs to run asap because some 3rd party set is_mobile in this hook - do_action('litespeed_control_finalize', defined('LSCACHE_IS_ESI') ? LSCACHE_IS_ESI : false); // Pass ESI block id + // Apply 3rd party filter. + // NOTE: Hook always needs to run asap because some 3rd party set is_mobile in this hook. + do_action( 'litespeed_control_finalize', defined( 'LSCACHE_IS_ESI' ) ? LSCACHE_IS_ESI : false ); // Pass ESI block id. - // if is not cacheable, terminate check - if (!self::is_cacheable()) { - self::debug('not cacheable after api_control'); + // if is not cacheable, terminate check. + if ( ! self::is_cacheable() ) { + self::debug( 'not cacheable after api_control' ); return; } - // Check litespeed setting to set cacheable status - if (!$this->_setting_cacheable()) { + // Check litespeed setting to set cacheable status. + if ( ! $this->_setting_cacheable() ) { self::set_nocache(); return; } - // If user has password cookie, do not cache (moved from vary) + // If user has password cookie, do not cache (moved from vary). global $post; - if (!empty($post->post_password) && isset($_COOKIE['wp-postpass_' . COOKIEHASH])) { - // If user has password cookie, do not cache - self::set_nocache('pswd cookie'); + if ( ! empty( $post->post_password ) && isset( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] ) ) { + self::set_nocache( 'pswd cookie' ); return; } - // The following check to the end is ONLY for mobile - $is_mobile = apply_filters('litespeed_is_mobile', false); - if (!$this->conf(Base::O_CACHE_MOBILE)) { - if ($is_mobile) { - self::set_nocache('mobile'); + // The following check to the end is ONLY for mobile. + $is_mobile_conf = apply_filters( 'litespeed_is_mobile', false ); + if ( ! $this->conf( Base::O_CACHE_MOBILE ) ) { + if ( $is_mobile_conf ) { + self::set_nocache( 'mobile' ); } return; } - $env_vary = isset($_SERVER['LSCACHE_VARY_VALUE']) ? $_SERVER['LSCACHE_VARY_VALUE'] : false; - if (!$env_vary) { - $env_vary = isset($_SERVER['HTTP_X_LSCACHE_VARY_VALUE']) ? $_SERVER['HTTP_X_LSCACHE_VARY_VALUE'] : false; + $env_vary = isset( $_SERVER['LSCACHE_VARY_VALUE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['LSCACHE_VARY_VALUE'] ) ) : ''; + if ( !$env_vary && isset( $_SERVER['HTTP_X_LSCACHE_VARY_VALUE'] ) ) { + $env_vary = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_LSCACHE_VARY_VALUE'] ) ); } - if ($env_vary && strpos($env_vary, 'ismobile') !== false) { - if (!wp_is_mobile() && !$is_mobile) { - self::set_nocache('is not mobile'); // todo: no need to uncache, it will correct vary value in vary finalize anyways + if ( $env_vary && false !== strpos( $env_vary, 'ismobile' ) ) { + if ( ! wp_is_mobile() && ! $is_mobile_conf ) { + self::set_nocache( 'is not mobile' ); // todo: no need to uncache, it will correct vary value in vary finalize anyways. return; } - } elseif (wp_is_mobile() || $is_mobile) { - self::set_nocache('is mobile'); + } elseif ( wp_is_mobile() || $is_mobile_conf ) { + self::set_nocache( 'is mobile' ); return; } } /** - * Check if is mobile for filter `litespeed_is_mobile` in API + * Check if is mobile for filter `litespeed_is_mobile` in API. * * @since 3.0 * @access public + * @return bool */ public static function is_mobile() { return wp_is_mobile(); } /** - * Get request method w/ compatibility to X-Http-Method-Override + * Get request method w/ compatibility to X-Http-Method-Override. * * @since 6.2 + * @return string */ private function _get_req_method() { - if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { - self::debug('X-Http-Method-Override -> ' . $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); - defined('LITESPEED_X_HTTP_METHOD_OVERRIDE') || define('LITESPEED_X_HTTP_METHOD_OVERRIDE', true); - return $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; + if ( isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ) { + $override = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ); + self::debug( 'X-Http-Method-Override -> ' . $override ); + if ( ! defined( 'LITESPEED_X_HTTP_METHOD_OVERRIDE' ) ) { + define( 'LITESPEED_X_HTTP_METHOD_OVERRIDE', true ); + } + return $override; } - if (isset($_SERVER['REQUEST_METHOD'])) { - return $_SERVER['REQUEST_METHOD']; + if ( isset( $_SERVER['REQUEST_METHOD'] ) ) { + return sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ); } return 'unknown'; } @@ -753,89 +828,90 @@ private function _get_req_method() { * * @since 1.0.0 * @access private - * @return boolean True if cacheable, false otherwise. + * @return bool True if cacheable, false otherwise. */ private function _setting_cacheable() { - // logged_in users already excluded, no hook added + // logged_in users already excluded, no hook added. - if (!empty($_REQUEST[Router::ACTION])) { - return $this->_no_cache_for('Query String Action'); + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( ! empty( $_REQUEST[ Router::ACTION ] ) ) { + return $this->_no_cache_for( 'Query String Action' ); } $method = $this->_get_req_method(); - if (defined('LITESPEED_X_HTTP_METHOD_OVERRIDE') && LITESPEED_X_HTTP_METHOD_OVERRIDE && $method == 'HEAD') { - return $this->_no_cache_for('HEAD method from override'); + if ( defined( 'LITESPEED_X_HTTP_METHOD_OVERRIDE' ) && LITESPEED_X_HTTP_METHOD_OVERRIDE && 'HEAD' === $method ) { + return $this->_no_cache_for( 'HEAD method from override' ); } - if ('GET' !== $method && 'HEAD' !== $method) { - return $this->_no_cache_for('Not GET method: ' . $method); + if ( 'GET' !== $method && 'HEAD' !== $method ) { + return $this->_no_cache_for( 'Not GET method: ' . $method ); } - if (is_feed() && $this->conf(Base::O_CACHE_TTL_FEED) == 0) { - return $this->_no_cache_for('feed'); + if ( is_feed() && 0 === $this->conf( Base::O_CACHE_TTL_FEED ) ) { + return $this->_no_cache_for( 'feed' ); } - if (is_trackback()) { - return $this->_no_cache_for('trackback'); + if ( is_trackback() ) { + return $this->_no_cache_for( 'trackback' ); } - if (is_search()) { - return $this->_no_cache_for('search'); + if ( is_search() ) { + return $this->_no_cache_for( 'search' ); } - // if ( !defined('WP_USE_THEMES') || !WP_USE_THEMES ) { - // return $this->_no_cache_for('no theme used'); - // } - - // Check private cache URI setting - $excludes = $this->conf(Base::O_CACHE_PRIV_URI); - $result = Utility::str_hit_array($_SERVER['REQUEST_URI'], $excludes); - if ($result) { - self::set_private('Admin cfg Private Cached URI: ' . $result); + // Check private cache URI setting. + $excludes = $this->conf( Base::O_CACHE_PRIV_URI ); + $req_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + $result = Utility::str_hit_array( $req_uri, $excludes ); + if ( $result ) { + self::set_private( 'Admin cfg Private Cached URI: ' . $result ); } - if (!self::is_forced_cacheable()) { - // Check if URI is excluded from cache - $excludes = $this->cls('Data')->load_cache_nocacheable($this->conf(Base::O_CACHE_EXC)); - $result = Utility::str_hit_array($_SERVER['REQUEST_URI'], $excludes); - if ($result) { - return $this->_no_cache_for('Admin configured URI Do not cache: ' . $result); + if ( ! self::is_forced_cacheable() ) { + // Check if URI is excluded from cache. + $excludes = $this->cls( 'Data' )->load_cache_nocacheable( $this->conf( Base::O_CACHE_EXC ) ); + $result = Utility::str_hit_array( $req_uri, $excludes ); + if ( $result ) { + return $this->_no_cache_for( 'Admin configured URI Do not cache: ' . $result ); } - // Check QS excluded setting - $excludes = $this->conf(Base::O_CACHE_EXC_QS); - if (!empty($excludes) && ($qs = $this->_is_qs_excluded($excludes))) { - return $this->_no_cache_for('Admin configured QS Do not cache: ' . $qs); + // Check QS excluded setting. + $excludes = $this->conf( Base::O_CACHE_EXC_QS ); + $qs_hit = $this->_is_qs_excluded( $excludes ); + if ( ! empty( $excludes ) && $qs_hit ) { + return $this->_no_cache_for( 'Admin configured QS Do not cache: ' . $qs_hit ); } - $excludes = $this->conf(Base::O_CACHE_EXC_CAT); - if (!empty($excludes) && has_category($excludes)) { - return $this->_no_cache_for('Admin configured Category Do not cache.'); + $excludes = $this->conf( Base::O_CACHE_EXC_CAT ); + if ( ! empty( $excludes ) && has_category( $excludes ) ) { + return $this->_no_cache_for( 'Admin configured Category Do not cache.' ); } - $excludes = $this->conf(Base::O_CACHE_EXC_TAG); - if (!empty($excludes) && has_tag($excludes)) { - return $this->_no_cache_for('Admin configured Tag Do not cache.'); + $excludes = $this->conf( Base::O_CACHE_EXC_TAG ); + if ( ! empty( $excludes ) && has_tag( $excludes ) ) { + return $this->_no_cache_for( 'Admin configured Tag Do not cache.' ); } - $excludes = $this->conf(Base::O_CACHE_EXC_COOKIES); - if (!empty($excludes) && !empty($_COOKIE)) { - $cookie_hit = array_intersect(array_keys($_COOKIE), $excludes); - if ($cookie_hit) { - return $this->_no_cache_for('Admin configured Cookie Do not cache.'); + $excludes = $this->conf( Base::O_CACHE_EXC_COOKIES ); + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- names only, compared as keys. + if ( ! empty( $excludes ) && ! empty( $_COOKIE ) ) { + $cookie_hit = array_intersect( array_keys( $_COOKIE ), $excludes ); + if ( $cookie_hit ) { + return $this->_no_cache_for( 'Admin configured Cookie Do not cache.' ); } } - $excludes = $this->conf(Base::O_CACHE_EXC_USERAGENTS); - if (!empty($excludes) && isset($_SERVER['HTTP_USER_AGENT'])) { - $nummatches = preg_match(Utility::arr2regex($excludes), $_SERVER['HTTP_USER_AGENT']); - if ($nummatches) { - return $this->_no_cache_for('Admin configured User Agent Do not cache.'); + $excludes = $this->conf( Base::O_CACHE_EXC_USERAGENTS ); + if ( ! empty( $excludes ) && isset( $_SERVER['HTTP_USER_AGENT'] ) ) { + $nummatches = preg_match( Utility::arr2regex( $excludes ), sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ); + if ( $nummatches ) { + return $this->_no_cache_for( 'Admin configured User Agent Do not cache.' ); } } - // Check if is exclude roles ( Need to set Vary too ) - if ($result = $this->in_cache_exc_roles()) { - return $this->_no_cache_for('Role Excludes setting ' . $result); + // Check if is exclude roles ( Need to set Vary too ). + $result = $this->in_cache_exc_roles(); + if ( $result ) { + return $this->_no_cache_for( 'Role Excludes setting ' . $result ); } } @@ -847,25 +923,33 @@ private function _setting_cacheable() { * * @since 1.0.0 * @access private + * * @param string $reason An explanation for why the page is not cacheable. - * @return boolean Return false. + * @return bool Always false. */ private function _no_cache_for( $reason ) { - self::debug('X Cache_control off - ' . $reason); + self::debug( 'X Cache_control off - ' . $reason ); return false; } /** - * Check if current request has qs excluded setting + * Check if current request has qs excluded setting. * * @since 1.3 * @access private - * @param array $excludes QS excludes setting - * @return boolean|string False if not excluded, otherwise the hit qs list + * + * @param array<int,string> $excludes QS excludes setting. + * @return bool|string False if not excluded, otherwise the hit qs list. */ private function _is_qs_excluded( $excludes ) { - if (!empty($_GET) && ($intersect = array_intersect(array_keys($_GET), $excludes))) { - return implode(',', $intersect); + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( ! empty( $_GET ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $keys = array_keys( $_GET ); + $intersect = array_intersect( $keys, $excludes ); + if ( $intersect ) { + return implode( ',', $intersect ); + } } return false; } diff --git a/src/core.cls.php b/src/core.cls.php index 02d3f8cfb..0b7c1cb3d 100644 --- a/src/core.cls.php +++ b/src/core.cls.php @@ -1,17 +1,25 @@ <?php - /** * The core plugin class. * + * This is the main class for the LiteSpeed Cache plugin, responsible for initializing + * the plugin's core functionality, registering hooks, and handling cache-related operations. + * * Note: Core doesn't allow $this->cls( 'Core' ) * - * @since 1.0.0 + * @since 1.0.0 + * @package LiteSpeed */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Core + * + * @since 1.0.0 + */ class Core extends Root { const NAME = 'LiteSpeed Cache'; @@ -23,7 +31,7 @@ class Core extends Root { const ACTION_PURGE_BY = 'PURGE_BY'; const ACTION_PURGE_EMPTYCACHE = 'PURGE_EMPTYCACHE'; const ACTION_QS_PURGE = 'PURGE'; - const ACTION_QS_PURGE_SINGLE = 'PURGESINGLE'; // This will be same as `ACTION_QS_PURGE` (purge single url only) + const ACTION_QS_PURGE_SINGLE = 'PURGESINGLE'; // This will be same as `ACTION_QS_PURGE` (purge single URL only) const ACTION_QS_SHOW_HEADERS = 'SHOWHEADERS'; const ACTION_QS_PURGE_ALL = 'purge_all'; const ACTION_QS_PURGE_EMPTYCACHE = 'empty_all'; @@ -31,9 +39,21 @@ class Core extends Root { const HEADER_DEBUG = 'X-LiteSpeed-Debug'; - protected static $_debug_show_header = false; + /** + * Whether to show debug headers. + * + * @var bool + * @since 1.0.0 + */ + protected static $debug_show_header = false; - private $_footer_comment = ''; + /** + * Footer comment buffer. + * + * @var string + * @since 1.0.0 + */ + private $footer_comment = ''; /** * Define the core functionality of the plugin. @@ -42,112 +62,110 @@ class Core extends Root { * Load the dependencies, define the locale, and set the hooks for the admin area and * the public-facing side of the site. * - * @since 1.0.0 + * @since 1.0.0 */ public function __construct() { - !defined('LSCWP_TS_0') && define('LSCWP_TS_0', microtime(true)); - $this->cls('Conf')->init(); + ! defined( 'LSCWP_TS_0' ) && define( 'LSCWP_TS_0', microtime( true ) ); + $this->cls( 'Conf' )->init(); /** * Load API hooks * - * @since 3.0 + * @since 3.0 */ - $this->cls('API')->init(); + $this->cls( 'API' )->init(); - if (defined('LITESPEED_ON')) { + if ( defined( 'LITESPEED_ON' ) ) { // Load third party detection if lscache enabled. include_once LSCWP_DIR . 'thirdparty/entry.inc.php'; } - if ($this->conf(Base::O_DEBUG_DISABLE_ALL)) { - !defined('LITESPEED_DISABLE_ALL') && define('LITESPEED_DISABLE_ALL', true); + + if ( $this->conf( Base::O_DEBUG_DISABLE_ALL ) || Debug2::is_tmp_disable() ) { + ! defined( 'LITESPEED_DISABLE_ALL' ) && define( 'LITESPEED_DISABLE_ALL', true ); } /** * Register plugin activate/deactivate/uninstall hooks - * NOTE: this can't be moved under after_setup_theme, otherwise activation will be bypassed somehow + * NOTE: this can't be moved under after_setup_theme, otherwise activation will be bypassed * - * @since 2.7.1 Disabled admin&CLI check to make frontend able to enable cache too + * @since 2.7.1 Disabled admin&CLI check to make frontend able to enable cache too */ - // if( is_admin() || defined( 'LITESPEED_CLI' ) ) { $plugin_file = LSCWP_DIR . 'litespeed-cache.php'; - register_activation_hook($plugin_file, array( __NAMESPACE__ . '\Activation', 'register_activation' )); - register_deactivation_hook($plugin_file, array( __NAMESPACE__ . '\Activation', 'register_deactivation' )); - register_uninstall_hook($plugin_file, __NAMESPACE__ . '\Activation::uninstall_litespeed_cache'); - // } + register_activation_hook( $plugin_file, array( __NAMESPACE__ . '\Activation', 'register_activation' ) ); + register_deactivation_hook( $plugin_file, array( __NAMESPACE__ . '\Activation', 'register_deactivation' ) ); + register_uninstall_hook( $plugin_file, __NAMESPACE__ . '\Activation::uninstall_litespeed_cache' ); - if (defined('LITESPEED_ON')) { - // register purge_all actions - $purge_all_events = $this->conf(Base::O_PURGE_HOOK_ALL); + if ( defined( 'LITESPEED_ON' ) ) { + // Register purge_all actions + $purge_all_events = $this->conf( Base::O_PURGE_HOOK_ALL ); - // purge all on upgrade - if ($this->conf(Base::O_PURGE_ON_UPGRADE)) { + // Purge all on upgrade + if ( $this->conf( Base::O_PURGE_ON_UPGRADE ) ) { $purge_all_events[] = 'automatic_updates_complete'; $purge_all_events[] = 'upgrader_process_complete'; $purge_all_events[] = 'admin_action_do-plugin-upgrade'; } - foreach ($purge_all_events as $event) { - // Don't allow hook to update_option bcos purge_all will cause infinite loop of update_option - if (in_array($event, array( 'update_option' ))) { + foreach ( $purge_all_events as $event ) { + // Don't allow hook to update_option because purge_all will cause infinite loop of update_option + if ( in_array( $event, array( 'update_option' ), true ) ) { continue; } - add_action($event, __NAMESPACE__ . '\Purge::purge_all'); + add_action( $event, __NAMESPACE__ . '\Purge::purge_all' ); } - // add_filter( 'upgrader_pre_download', 'Purge::filter_with_purge_all' ); // Add headers to site health check for full page cache // @since 5.4 - add_filter('site_status_page_cache_supported_cache_headers', function ( $cache_headers ) { + add_filter( 'site_status_page_cache_supported_cache_headers', function ( $cache_headers ) { $is_cache_hit = function ( $header_value ) { - return false !== strpos(strtolower($header_value), 'hit'); + return false !== strpos( strtolower( $header_value ), 'hit' ); }; $cache_headers['x-litespeed-cache'] = $is_cache_hit; $cache_headers['x-lsadc-cache'] = $is_cache_hit; $cache_headers['x-qc-cache'] = $is_cache_hit; return $cache_headers; - }); + } ); } - add_action('after_setup_theme', array( $this, 'init' )); + add_action( 'after_setup_theme', array( $this, 'init' ) ); // Check if there is a purge request in queue - if (!defined('LITESPEED_CLI')) { - $purge_queue = Purge::get_option(Purge::DB_QUEUE); - if ($purge_queue && $purge_queue != -1) { - $this->_http_header($purge_queue); - Debug2::debug('[Core] Purge Queue found&sent: ' . $purge_queue); + if ( ! defined( 'LITESPEED_CLI' ) ) { + $purge_queue = Purge::get_option( Purge::DB_QUEUE ); + if ( $purge_queue && '-1' !== $purge_queue ) { + $this->http_header( $purge_queue ); + Debug2::debug( '[Core] Purge Queue found&sent: ' . $purge_queue ); } - if ($purge_queue != -1) { - Purge::update_option(Purge::DB_QUEUE, -1); // Use 0 to bypass purge while still enable db update as WP's update_option will check value===false to bypass update + if ( '-1' !== $purge_queue ) { + Purge::update_option( Purge::DB_QUEUE, '-1' ); // Use -1 to bypass purge while still enable db update as WP's update_option will check value===false to bypass update } - $purge_queue = Purge::get_option(Purge::DB_QUEUE2); - if ($purge_queue && $purge_queue != -1) { - $this->_http_header($purge_queue); - Debug2::debug('[Core] Purge2 Queue found&sent: ' . $purge_queue); + $purge_queue = Purge::get_option( Purge::DB_QUEUE2 ); + if ( $purge_queue && '-1' !== $purge_queue ) { + $this->http_header( $purge_queue ); + Debug2::debug( '[Core] Purge2 Queue found&sent: ' . $purge_queue ); } - if ($purge_queue != -1) { - Purge::update_option(Purge::DB_QUEUE2, -1); + if ( '-1' !== $purge_queue ) { + Purge::update_option( Purge::DB_QUEUE2, '-1' ); } } /** * Hook internal REST * - * @since 2.9.4 + * @since 2.9.4 */ - $this->cls('REST'); + $this->cls( 'REST' ); /** * Hook wpnonce function * * Note: ESI nonce won't be available until hook after_setup_theme ESI init due to Guest Mode concern * - * @since v4.1 + * @since 4.1 */ - if ($this->cls('Router')->esi_enabled() && !function_exists('wp_create_nonce')) { - Debug2::debug('[ESI] Overwrite wp_create_nonce()'); + if ( $this->cls( 'Router' )->esi_enabled() && ! function_exists( 'wp_create_nonce' ) ) { + Debug2::debug( '[ESI] Overwrite wp_create_nonce()' ); litespeed_define_nonce_func(); } } @@ -160,140 +178,128 @@ public function __construct() { * NOTE: WP user doesn't init yet * * @since 1.0.0 - * @access public */ public function init() { /** * Added hook before init * 3rd party preload hooks will be fired here too (e.g. Divi disable all in edit mode) * - * @since 1.6.6 - * @since 2.6 Added filter to all config values in Conf + * @since 1.6.6 + * @since 2.6 Added filter to all config values in Conf */ - do_action('litespeed_init'); - add_action('wp_ajax_async_litespeed', 'LiteSpeed\Task::async_litespeed_handler'); - add_action('wp_ajax_nopriv_async_litespeed', 'LiteSpeed\Task::async_litespeed_handler'); + do_action( 'litespeed_init' ); + add_action( 'wp_ajax_async_litespeed', 'LiteSpeed\Task::async_litespeed_handler' ); + add_action( 'wp_ajax_nopriv_async_litespeed', 'LiteSpeed\Task::async_litespeed_handler' ); - // in `after_setup_theme`, before `init` hook - $this->cls('Activation')->auto_update(); + // In `after_setup_theme`, before `init` hook + $this->cls( 'Activation' )->auto_update(); - if (is_admin() && !wp_doing_ajax()) { - $this->cls('Admin'); + if ( is_admin() && ! wp_doing_ajax() ) { + $this->cls( 'Admin' ); } - if (defined('LITESPEED_DISABLE_ALL') && LITESPEED_DISABLE_ALL) { - Debug2::debug('[Core] Bypassed due to debug disable all setting'); + if ( defined( 'LITESPEED_DISABLE_ALL' ) && LITESPEED_DISABLE_ALL ) { + Debug2::debug( '[Core] Bypassed due to debug disable all setting' ); return; } - do_action('litespeed_initing'); + do_action( 'litespeed_initing' ); - ob_start(array( $this, 'send_headers_force' )); - add_action('shutdown', array( $this, 'send_headers' ), 0); - add_action('wp_footer', array( $this, 'footer_hook' )); + ob_start( array( $this, 'send_headers_force' ) ); + add_action( 'shutdown', array( $this, 'send_headers' ), 0 ); + add_action( 'wp_footer', array( $this, 'footer_hook' ) ); /** - * Check if is non optm simulator + * Check if is non-optimization simulator * - * @since 2.9 + * @since 2.9 */ - if (!empty($_GET[Router::ACTION]) && $_GET[Router::ACTION] == 'before_optm' && !apply_filters('litespeed_qs_forbidden', false)) { - Debug2::debug('[Core] ⛑️ bypass_optm due to QS CTRL'); - !defined('LITESPEED_NO_OPTM') && define('LITESPEED_NO_OPTM', true); + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( ! empty( $_GET[ Router::ACTION ] ) && 'before_optm' === $_GET[ Router::ACTION ] && ! apply_filters( 'litespeed_qs_forbidden', false ) ) { + Debug2::debug( '[Core] ⛑️ bypass_optm due to QS CTRL' ); + ! defined( 'LITESPEED_NO_OPTM' ) && define( 'LITESPEED_NO_OPTM', true ); } /** * Register vary filter * - * @since 1.6.2 + * @since 1.6.2 */ - $this->cls('Control')->init(); - - // 1. Init vary - // 2. Init cacheable status - // $this->cls('Vary')->init(); + $this->cls( 'Control' )->init(); // Init Purge hooks - $this->cls('Purge')->init(); + $this->cls( 'Purge' )->init(); - $this->cls('Tag')->init(); + $this->cls( 'Tag' )->init(); // Load hooks that may be related to users - add_action('init', array( $this, 'after_user_init' ), 5); + add_action( 'init', array( $this, 'after_user_init' ), 5 ); // Load 3rd party hooks - add_action('wp_loaded', array( $this, 'load_thirdparty' ), 2); - - // test: Simulate a purge all - // if (defined( 'LITESPEED_CLI' )) Purge::add('test'.date('Ymd.His')); + add_action( 'wp_loaded', array( $this, 'load_thirdparty' ), 2 ); } /** * Run hooks after user init * * @since 2.9.8 - * @access public */ public function after_user_init() { - $this->cls('Router')->is_role_simulation(); + $this->cls( 'Router' )->is_role_simulation(); - // Detect if is Guest mode or not also - $this->cls('Vary')->after_user_init(); + // Detect if is Guest mode or not + $this->cls( 'Vary' )->after_user_init(); // Register attachment delete hook - $this->cls('Media')->after_user_init(); + $this->cls( 'Media' )->after_user_init(); /** - * Preload ESI functionality for ESI request uri recovery + * Preload ESI functionality for ESI request URI recovery * * @since 1.8.1 - * @since 4.0 ESI init needs to be after Guest mode detection to bypass ESI if is under Guest mode + * @since 4.0 ESI init needs to be after Guest mode detection to bypass ESI if is under Guest mode */ - $this->cls('ESI')->init(); + $this->cls( 'ESI' )->init(); - if (!is_admin() && !defined('LITESPEED_GUEST_OPTM') && ($result = $this->cls('Conf')->in_optm_exc_roles())) { - Debug2::debug('[Core] ⛑️ bypass_optm: hit Role Excludes setting: ' . $result); - !defined('LITESPEED_NO_OPTM') && define('LITESPEED_NO_OPTM', true); + if ( ! is_admin() && ! defined( 'LITESPEED_GUEST_OPTM' ) ) { + $result = $this->cls( 'Conf' )->in_optm_exc_roles(); + if ( $result ) { + Debug2::debug( '[Core] ⛑️ bypass_optm: hit Role Excludes setting: ' . $result ); + ! defined( 'LITESPEED_NO_OPTM' ) && define( 'LITESPEED_NO_OPTM', true ); + } } // Heartbeat control - $this->cls('Tool')->heartbeat(); + $this->cls( 'Tool' )->heartbeat(); - /** - * Backward compatibility for v4.2- @Ruikai - * TODO: Will change to hook in future versions to make it revertable - */ - if (defined('LITESPEED_BYPASS_OPTM') && !defined('LITESPEED_NO_OPTM')) { - define('LITESPEED_NO_OPTM', LITESPEED_BYPASS_OPTM); - } - - if (!defined('LITESPEED_NO_OPTM') || !LITESPEED_NO_OPTM) { + if ( ! defined( 'LITESPEED_NO_OPTM' ) || ! LITESPEED_NO_OPTM ) { // Check missing static files - $this->cls('Router')->serve_static(); + $this->cls( 'Router' )->serve_static(); - $this->cls('Media')->init(); + $this->cls( 'Media' )->init(); - $this->cls('Placeholder')->init(); + $this->cls( 'Placeholder' )->init(); - $this->cls('Router')->can_optm() && $this->cls('Optimize')->init(); + $this->cls( 'Router' )->can_optm() && $this->cls( 'Optimize' )->init(); - $this->cls('Localization')->init(); + $this->cls( 'Localization' )->init(); - // Hook cdn for attachments - $this->cls('CDN')->init(); + // Hook CDN for attachments + $this->cls( 'CDN' )->init(); - // load cron tasks - $this->cls('Task')->init(); + // Load cron tasks + $this->cls( 'Task' )->init(); } - // load litespeed actions - if ($action = Router::get_action()) { - $this->proceed_action($action); + // Load litespeed actions + $action = Router::get_action(); + if ( $action ) { + $this->proceed_action( $action ); } // Load frontend GUI - if (!is_admin()) { - $this->cls('GUI')->init(); + if ( ! is_admin() ) { + $this->cls( 'GUI' )->init(); } } @@ -301,52 +307,52 @@ public function after_user_init() { * Run frontend actions * * @since 1.1.0 - * @access public + * @param string $action The action to proceed. */ public function proceed_action( $action ) { $msg = false; - // handle actions - switch ($action) { + // Handle actions + switch ( $action ) { case self::ACTION_QS_SHOW_HEADERS: - self::$_debug_show_header = true; + self::$debug_show_header = true; break; case self::ACTION_QS_PURGE: case self::ACTION_QS_PURGE_SINGLE: - Purge::set_purge_single(); + Purge::set_purge_single(); break; case self::ACTION_QS_PURGE_ALL: - Purge::purge_all(); + Purge::purge_all(); break; case self::ACTION_PURGE_EMPTYCACHE: case self::ACTION_QS_PURGE_EMPTYCACHE: - define('LSWCP_EMPTYCACHE', true); // clear all sites caches - Purge::purge_all(); - $msg = __('Notified LiteSpeed Web Server to purge everything.', 'litespeed-cache'); + define( 'LSWCP_EMPTYCACHE', true ); // Clear all sites caches + Purge::purge_all(); + $msg = __( 'Notified LiteSpeed Web Server to purge everything.', 'litespeed-cache' ); break; case self::ACTION_PURGE_BY: - $this->cls('Purge')->purge_list(); - $msg = __('Notified LiteSpeed Web Server to purge the list.', 'litespeed-cache'); + $this->cls( 'Purge' )->purge_list(); + $msg = __( 'Notified LiteSpeed Web Server to purge the list.', 'litespeed-cache' ); break; - case self::ACTION_DISMISS: // Even its from ajax, we don't need to register wp ajax callback function but directly use our action - GUI::dismiss(); + case self::ACTION_DISMISS: + GUI::dismiss(); break; default: - $msg = $this->cls('Router')->handler($action); + $msg = $this->cls( 'Router' )->handler( $action ); break; } - if ($msg && !Router::is_ajax()) { - Admin_Display::add_notice(Admin_Display::NOTICE_GREEN, $msg); + if ( $msg && ! Router::is_ajax() ) { + Admin_Display::add_notice( Admin_Display::NOTICE_GREEN, $msg ); Admin::redirect(); return; } - if (Router::is_ajax()) { + if ( Router::is_ajax() ) { exit(); } } @@ -357,22 +363,20 @@ public function proceed_action( $action ) { * The detect action is used by third party plugin integration classes to determine if they should add the rest of their hooks. * * @since 1.0.5 - * @access public */ public function load_thirdparty() { - do_action('litespeed_load_thirdparty'); + do_action( 'litespeed_load_thirdparty' ); } /** * Mark wp_footer called * * @since 1.3 - * @access public */ public function footer_hook() { - Debug2::debug('[Core] Footer hook called'); - if (!defined('LITESPEED_FOOTER_CALLED')) { - define('LITESPEED_FOOTER_CALLED', true); + Debug2::debug( '[Core] Footer hook called' ); + if ( ! defined( 'LITESPEED_FOOTER_CALLED' ) ) { + define( 'LITESPEED_FOOTER_CALLED', true ); } } @@ -380,150 +384,150 @@ public function footer_hook() { * Trigger comment info display hook * * @since 1.3 - * @access private + * @param string|null $buffer The buffer to check. + * @return void */ - private function _check_is_html( $buffer = null ) { - if (!defined('LITESPEED_FOOTER_CALLED')) { - Debug2::debug2('[Core] CHK html bypass: miss footer const'); + private function check_is_html( $buffer = null ) { + if ( ! defined( 'LITESPEED_FOOTER_CALLED' ) ) { + Debug2::debug2( '[Core] CHK html bypass: miss footer const' ); return; } - if (wp_doing_ajax()) { - Debug2::debug2('[Core] CHK html bypass: doing ajax'); + if ( wp_doing_ajax() ) { + Debug2::debug2( '[Core] CHK html bypass: doing ajax' ); return; } - if (wp_doing_cron()) { - Debug2::debug2('[Core] CHK html bypass: doing cron'); + if ( wp_doing_cron() ) { + Debug2::debug2( '[Core] CHK html bypass: doing cron' ); return; } - if ($_SERVER['REQUEST_METHOD'] !== 'GET') { - Debug2::debug2('[Core] CHK html bypass: not get method ' . $_SERVER['REQUEST_METHOD']); + if ( empty( $_SERVER['REQUEST_METHOD'] ) || 'GET' !== $_SERVER['REQUEST_METHOD'] ) { + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + Debug2::debug2( '[Core] CHK html bypass: not get method ' . wp_unslash( $_SERVER['REQUEST_METHOD'] ) ); return; } - if ($buffer === null) { + if ( null === $buffer ) { $buffer = ob_get_contents(); } - // double check to make sure it is a html file - if (strlen($buffer) > 300) { - $buffer = substr($buffer, 0, 300); + // Double check to make sure it is an HTML file + if ( strlen( $buffer ) > 300 ) { + $buffer = substr( $buffer, 0, 300 ); } - if (strstr($buffer, '<!--') !== false) { - $buffer = preg_replace('/<!--.*?-->/s', '', $buffer); + if ( false !== strstr( $buffer, '<!--' ) ) { + $buffer = preg_replace( '/<!--.*?-->/s', '', $buffer ); } - $buffer = trim($buffer); + $buffer = trim( $buffer ); - $buffer = File::remove_zero_space($buffer); + $buffer = File::remove_zero_space( $buffer ); - $is_html = stripos($buffer, '<html') === 0 || stripos($buffer, '<!DOCTYPE') === 0; + $is_html = 0 === stripos( $buffer, '<html' ) || 0 === stripos( $buffer, '<!DOCTYPE' ); - if (!$is_html) { - Debug2::debug('[Core] Footer check failed: ' . ob_get_level() . '-' . substr($buffer, 0, 100)); + if ( ! $is_html ) { + Debug2::debug( '[Core] Footer check failed: ' . ob_get_level() . '-' . substr( $buffer, 0, 100 ) ); return; } - Debug2::debug('[Core] Footer check passed'); + Debug2::debug( '[Core] Footer check passed' ); - if (!defined('LITESPEED_IS_HTML')) { - define('LITESPEED_IS_HTML', true); + if ( ! defined( 'LITESPEED_IS_HTML' ) ) { + define( 'LITESPEED_IS_HTML', true ); } } /** - * For compatibility with those plugins have 'Bad' logic that forced all buffer output even it is NOT their buffer :( + * For compatibility with plugins that have 'Bad' logic that forced all buffer output even if it is NOT their buffer. * * Usually this is called after send_headers() if following original WP process * * @since 1.1.5 - * @access public - * @param string $buffer - * @return string + * @param string $buffer The buffer to process. + * @return string The processed buffer. */ public function send_headers_force( $buffer ) { - $this->_check_is_html($buffer); + $this->check_is_html( $buffer ); // Hook to modify buffer before - $buffer = apply_filters('litespeed_buffer_before', $buffer); + $buffer = apply_filters( 'litespeed_buffer_before', $buffer ); /** * Media: Image lazyload && WebP - * GUI: Clean wrapper mainly for esi block NOTE: this needs to be before optimizer to avoid wrapper being removed + * GUI: Clean wrapper mainly for ESI block NOTE: this needs to be before optimizer to avoid wrapper being removed * Optimize * CDN */ - if (!defined('LITESPEED_NO_OPTM') || !LITESPEED_NO_OPTM) { - Debug2::debug('[Core] run hook litespeed_buffer_finalize'); - $buffer = apply_filters('litespeed_buffer_finalize', $buffer); + if ( ! defined( 'LITESPEED_NO_OPTM' ) || ! LITESPEED_NO_OPTM ) { + Debug2::debug( '[Core] run hook litespeed_buffer_finalize' ); + $buffer = apply_filters( 'litespeed_buffer_finalize', $buffer ); } /** * Replace ESI preserved list * - * @since 3.3 Replace this in the end to avoid `Inline JS Defer` or other Page Optm features encoded ESI tags wrongly, which caused LSWS can't recognize ESI + * @since 3.3 Replace this in the end to avoid `Inline JS Defer` or other Page Optm features encoded ESI tags wrongly, which caused LSWS can't recognize ESI */ - $buffer = $this->cls('ESI')->finalize($buffer); + $buffer = $this->cls( 'ESI' )->finalize( $buffer ); - $this->send_headers(true); + $this->send_headers( true ); // Log ESI nonce buffer empty issue - if (defined('LSCACHE_IS_ESI') && strlen($buffer) == 0) { - // log ref for debug purpose - error_log('ESI buffer empty ' . $_SERVER['REQUEST_URI']); + if ( defined( 'LSCACHE_IS_ESI' ) && 0 === strlen( $buffer ) && ! empty( $_SERVER['REQUEST_URI'] ) ) { + // Log ref for debug purpose + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.PHP.DevelopmentFunctions.error_log_error_log + error_log( 'ESI buffer empty ' . wp_unslash( $_SERVER['REQUEST_URI'] ) ); } // Init comment info - $running_info_showing = defined('LITESPEED_IS_HTML') || defined('LSCACHE_IS_ESI'); - if (defined('LSCACHE_ESI_SILENCE')) { + $running_info_showing = defined( 'LITESPEED_IS_HTML' ) || defined( 'LSCACHE_IS_ESI' ); + if ( defined( 'LSCACHE_ESI_SILENCE' ) ) { $running_info_showing = false; - Debug2::debug('[Core] ESI silence'); + Debug2::debug( '[Core] ESI silence' ); } /** - * Silence comment for json req + * Silence comment for JSON request * * @since 2.9.3 */ - if (REST::cls()->is_rest() || Router::is_ajax()) { + if ( REST::cls()->is_rest() || Router::is_ajax() ) { $running_info_showing = false; - Debug2::debug('[Core] Silence Comment due to REST/AJAX'); + Debug2::debug( '[Core] Silence Comment due to REST/AJAX' ); } - $running_info_showing = apply_filters('litespeed_comment', $running_info_showing); - if ($running_info_showing) { - if ($this->_footer_comment) { - $buffer .= $this->_footer_comment; - } + $running_info_showing = apply_filters( 'litespeed_comment', $running_info_showing ); + if ( $running_info_showing && $this->footer_comment ) { + $buffer .= $this->footer_comment; } /** - * If ESI req is JSON, give the content JSON format + * If ESI request is JSON, give the content JSON format * - * @since 2.9.3 - * @since 2.9.4 ESI req could be from internal REST call, so moved json_encode out of this cond + * @since 2.9.3 + * @since 2.9.4 ESI request could be from internal REST call, so moved json_encode out of this condition */ - if (defined('LSCACHE_IS_ESI')) { - Debug2::debug('[Core] ESI Start 👇'); - if (strlen($buffer) > 500) { - Debug2::debug(trim(substr($buffer, 0, 500)) . '.....'); + if ( defined( 'LSCACHE_IS_ESI' ) ) { + Debug2::debug( '[Core] ESI Start 👇' ); + if ( strlen( $buffer ) > 500 ) { + Debug2::debug( trim( substr( $buffer, 0, 500 ) ) . '.....' ); } else { - Debug2::debug($buffer); + Debug2::debug( $buffer ); } - Debug2::debug('[Core] ESI End 👆'); + Debug2::debug( '[Core] ESI End 👆' ); } - if (apply_filters('litespeed_is_json', false)) { - if (\json_decode($buffer, true) == null) { - Debug2::debug('[Core] Buffer converting to JSON'); - $buffer = \json_encode($buffer); - $buffer = trim($buffer, '"'); + if ( apply_filters( 'litespeed_is_json', false ) ) { + if ( null === \json_decode( $buffer, true ) ) { + Debug2::debug( '[Core] Buffer converting to JSON' ); + $buffer = wp_json_encode( $buffer ); + $buffer = trim( $buffer, '"' ); } else { - Debug2::debug('[Core] JSON Buffer'); + Debug2::debug( '[Core] JSON Buffer' ); } } // Hook to modify buffer after - $buffer = apply_filters('litespeed_buffer_after', $buffer); + $buffer = apply_filters( 'litespeed_buffer_after', $buffer ); Debug2::ended(); @@ -536,160 +540,156 @@ public function send_headers_force( $buffer ) { * This will send out all LiteSpeed Cache related response headers needed for the post. * * @since 1.0.5 - * @access public - * @param boolean $is_forced If the header is sent following our normal finalizing logic + * @param bool $is_forced If the header is sent following our normal finalizing logic. */ public function send_headers( $is_forced = false ) { - // Make sure header output only run once - if (!defined('LITESPEED_DID_' . __FUNCTION__)) { - define('LITESPEED_DID_' . __FUNCTION__, true); - } else { + // Make sure header output only runs once + if ( defined( 'LITESPEED_DID_' . __FUNCTION__ ) ) { return; } + define( 'LITESPEED_DID_' . __FUNCTION__, true ); - // Avoid PHP warning for header sent out already - if (headers_sent()) { - self::debug('❌ !!! Err: Header sent out already'); + // Avoid PHP warning for headers sent out already + if ( headers_sent() ) { + self::debug( '❌ !!! Err: Header sent out already' ); return; } - $this->_check_is_html(); + $this->check_is_html(); - // NOTE: cache ctrl output needs to be done first, as currently some varies are added in 3rd party hook `litespeed_api_control`. - $this->cls('Control')->finalize(); + // Cache control output needs to be done first, as some varies are added in 3rd party hook `litespeed_api_control`. + $this->cls( 'Control' )->finalize(); - $vary_header = $this->cls('Vary')->finalize(); + $vary_header = $this->cls( 'Vary' )->finalize(); - // If is not cacheable but Admin QS is `purge` or `purgesingle`, `tag` still needs to be generated - $tag_header = $this->cls('Tag')->output(); - if (!$tag_header && Control::is_cacheable()) { - Control::set_nocache('empty tag header'); + // If not cacheable but Admin QS is `purge` or `purgesingle`, `tag` still needs to be generated + $tag_header = $this->cls( 'Tag' )->output(); + if ( ! $tag_header && Control::is_cacheable() ) { + Control::set_nocache( 'empty tag header' ); } - // NOTE: `purge` output needs to be after `tag` output as Admin QS may need to send `tag` header + // `Purge` output needs to be after `tag` output as Admin QS may need to send `tag` header $purge_header = Purge::output(); - // generate `control` header in the end in case control status is changed by other headers. - $control_header = $this->cls('Control')->output(); + // Generate `control` header in the end in case control status is changed by other headers + $control_header = $this->cls( 'Control' )->output(); - // Give one more break to avoid ff crash - if (!defined('LSCACHE_IS_ESI')) { - $this->_footer_comment .= "\n"; + // Give one more break to avoid Firefox crash + if ( ! defined( 'LSCACHE_IS_ESI' ) ) { + $this->footer_comment .= "\n"; } $cache_support = 'supported'; - if (defined('LITESPEED_ON')) { + if ( defined( 'LITESPEED_ON' ) ) { $cache_support = Control::is_cacheable() ? 'cached' : 'uncached'; } - $this->_comment( + $this->comment( sprintf( '%1$s %2$s by LiteSpeed Cache %4$s on %3$s', - defined('LSCACHE_IS_ESI') ? 'Block' : 'Page', + defined( 'LSCACHE_IS_ESI' ) ? 'Block' : 'Page', $cache_support, - date('Y-m-d H:i:s', time() + LITESPEED_TIME_OFFSET), + gmdate( 'Y-m-d H:i:s', time() + LITESPEED_TIME_OFFSET ), self::VER ) ); - // send Control header - if (defined('LITESPEED_ON') && $control_header) { - $this->_http_header($control_header); - if (!Control::is_cacheable()) { - $this->_http_header('Cache-Control: no-cache, no-store, must-revalidate, max-age=0'); // @ref: https://wordpress.org/support/topic/apply_filterslitespeed_control_cacheable-returns-false-for-cacheable/ + // Send Control header + if ( defined( 'LITESPEED_ON' ) && $control_header ) { + $this->http_header( $control_header ); + if ( ! Control::is_cacheable() && !is_admin() ) { + $ori_wp_header = wp_get_nocache_headers(); + if ( isset( $ori_wp_header['Cache-Control'] ) ) { + $this->http_header( 'Cache-Control: ' . $ori_wp_header['Cache-Control'] ); // @ref: https://github.com/litespeedtech/lscache_wp/issues/889 + } } - if (defined('LSCWP_LOG')) { - $this->_comment($control_header); + if ( defined( 'LSCWP_LOG' ) ) { + $this->comment( $control_header ); } } - // send PURGE header (Always send regardless of cache setting disabled/enabled) - if (defined('LITESPEED_ON') && $purge_header) { - $this->_http_header($purge_header); - Debug2::log_purge($purge_header); - if (defined('LSCWP_LOG')) { - $this->_comment($purge_header); + // Send PURGE header (Always send regardless of cache setting disabled/enabled) + if ( defined( 'LITESPEED_ON' ) && $purge_header ) { + $this->http_header( $purge_header ); + Debug2::log_purge( $purge_header ); + + if ( defined( 'LSCWP_LOG' ) ) { + $this->comment( $purge_header ); } } - // send Vary header - if (defined('LITESPEED_ON') && $vary_header) { - $this->_http_header($vary_header); - if (defined('LSCWP_LOG')) { - $this->_comment($vary_header); + + // Send Vary header + if ( defined( 'LITESPEED_ON' ) && $vary_header ) { + $this->http_header( $vary_header ); + if ( defined( 'LSCWP_LOG' ) ) { + $this->comment( $vary_header ); } } - if (defined('LITESPEED_ON') && defined('LSCWP_LOG')) { - $vary = $this->cls('Vary')->finalize_full_varies(); - if ($vary) { - $this->_comment('Full varies: ' . $vary); + if ( defined( 'LITESPEED_ON' ) && defined( 'LSCWP_LOG' ) ) { + $vary = $this->cls( 'Vary' )->finalize_full_varies(); + if ( $vary ) { + $this->comment( 'Full varies: ' . $vary ); } } // Admin QS show header action - if (self::$_debug_show_header) { + if ( self::$debug_show_header ) { $debug_header = self::HEADER_DEBUG . ': '; - if ($control_header) { + if ( $control_header ) { $debug_header .= $control_header . '; '; } - if ($purge_header) { + if ( $purge_header ) { $debug_header .= $purge_header . '; '; } - if ($tag_header) { + if ( $tag_header ) { $debug_header .= $tag_header . '; '; } - if ($vary_header) { + if ( $vary_header ) { $debug_header .= $vary_header . '; '; } - $this->_http_header($debug_header); - } else { - // Control header - if (defined('LITESPEED_ON') && Control::is_cacheable() && $tag_header) { - $this->_http_header($tag_header); - if (defined('LSCWP_LOG')) { - $this->_comment($tag_header); - } + $this->http_header( $debug_header ); + } elseif ( defined( 'LITESPEED_ON' ) && Control::is_cacheable() && $tag_header ) { + $this->http_header( $tag_header ); + if ( defined( 'LSCWP_LOG' ) ) { + $this->comment( $tag_header ); } } - // Object cache _comment - if (defined('LSCWP_LOG') && defined('LSCWP_OBJECT_CACHE') && method_exists('WP_Object_Cache', 'debug')) { - $this->_comment('Object Cache ' . \WP_Object_Cache::get_instance()->debug()); + // Object cache comment + if ( defined( 'LSCWP_LOG' ) && defined( 'LSCWP_OBJECT_CACHE' ) && method_exists( 'WP_Object_Cache', 'debug' ) ) { + $this->comment( 'Object Cache ' . \WP_Object_Cache::get_instance()->debug() ); } - if (defined('LITESPEED_GUEST') && LITESPEED_GUEST) { - $this->_comment('Guest Mode'); + if ( defined( 'LITESPEED_GUEST' ) && LITESPEED_GUEST ) { + $this->comment( 'Guest Mode' ); } - if (!empty($this->_footer_comment)) { - self::debug('[footer comment] ' . $this->_footer_comment); + if ( ! empty( $this->footer_comment ) ) { + self::debug( "[footer comment]\n" . trim( $this->footer_comment ) ); } - if ($is_forced) { - Debug2::debug('--forced--'); + if ( $is_forced ) { + Debug2::debug( '--forced--' ); } - /** - * If is CLI and contains Purge Header, then issue a HTTP req to Purge - * - * @since v5.3 - */ - if (defined('LITESPEED_CLI')) { - $purge_queue = Purge::get_option(Purge::DB_QUEUE); - if (!$purge_queue || $purge_queue == -1) { - $purge_queue = Purge::get_option(Purge::DB_QUEUE2); + // If CLI and contains Purge Header, issue an HTTP request to Purge + if ( defined( 'LITESPEED_CLI' ) ) { + $purge_queue = Purge::get_option( Purge::DB_QUEUE ); + if ( ! $purge_queue || '-1' === $purge_queue ) { + $purge_queue = Purge::get_option( Purge::DB_QUEUE2 ); } - if ($purge_queue && $purge_queue != -1) { - self::debug('[Core] Purge Queue found, issue a HTTP req to purge: ' . $purge_queue); - // Kick off HTTP req - $url = admin_url('admin-ajax.php'); - $resp = wp_safe_remote_get($url); - if (is_wp_error($resp)) { + if ( $purge_queue && '-1' !== $purge_queue ) { + self::debug( '[Core] Purge Queue found, issue an HTTP request to purge: ' . $purge_queue ); + // Kick off HTTP request + $url = admin_url( 'admin-ajax.php' ); + $resp = wp_safe_remote_get( $url ); + if ( is_wp_error( $resp ) ) { $error_message = $resp->get_error_message(); - self::debug('[URL]' . $url); - self::debug('failed to request: ' . $error_message); + self::debug( '[URL]' . $url ); + self::debug( 'failed to request: ' . $error_message ); } else { - self::debug('HTTP req res: ' . $resp['body']); + self::debug( 'HTTP request response: ' . $resp['body'] ); } } } @@ -699,30 +699,40 @@ public function send_headers( $is_forced = false ) { * Append one HTML comment * * @since 5.5 + * @param string $data The comment data. */ public static function comment( $data ) { - self::cls()->_comment($data); + self::cls()->append_comment( $data ); } - private function _comment( $data ) { - $this->_footer_comment .= "\n<!-- " . $data . ' -->'; + /** + * Append one HTML comment + * + * @since 5.5 + * @param string $data The comment data. + */ + private function append_comment( $data ) { + $this->footer_comment .= "\n<!-- " . $data . ' -->'; } /** * Send HTTP header * * @since 5.3 + * @param string $header The header to send. */ - private function _http_header( $header ) { - if (defined('LITESPEED_CLI')) { + private function http_header( $header ) { + if ( defined( 'LITESPEED_CLI' ) ) { return; } - @header($header); + if ( ! headers_sent() ) { + header( $header ); + } - if (!defined('LSCWP_LOG')) { + if ( ! defined( 'LSCWP_LOG' ) ) { return; } - Debug2::debug('💰 ' . $header); + Debug2::debug( '💰 ' . $header ); } } diff --git a/src/crawler-map.cls.php b/src/crawler-map.cls.php index f5eb1e3ef..a2a38d43b 100644 --- a/src/crawler-map.cls.php +++ b/src/crawler-map.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Crawler Sitemap Class @@ -35,7 +36,7 @@ public function __construct() { $this->__data = Data::cls(); $this->_tb = $this->__data->tb('crawler'); $this->_tb_blacklist = $this->__data->tb('crawler_blacklist'); - $this->_conf_map_timeout = defined('LITESPEED_CRAWLER_MAP_TIMEOUT') ? LITESPEED_CRAWLER_MAP_TIMEOUT : 180; // Specify the timeout while parsing the sitemap + $this->_conf_map_timeout = defined('LITESPEED_CRAWLER_MAP_TIMEOUT') ? constant('LITESPEED_CRAWLER_MAP_TIMEOUT') : 180; // Specify the timeout while parsing the sitemap } /** @@ -412,7 +413,7 @@ private function _gen() { } if (is_array($this->_urls) && !empty($this->_urls)) { - if (defined('LITESPEED_CRAWLER_DROP_DOMAIN') && LITESPEED_CRAWLER_DROP_DOMAIN) { + if (defined('LITESPEED_CRAWLER_DROP_DOMAIN') && constant('LITESPEED_CRAWLER_DROP_DOMAIN')) { foreach ($this->_urls as $k => $v) { if (stripos($v, $this->_site_url) !== 0) { unset($this->_urls[$k]); diff --git a/src/crawler.cls.php b/src/crawler.cls.php index 13fed6e5c..b0870db33 100644 --- a/src/crawler.cls.php +++ b/src/crawler.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The crawler class @@ -394,8 +395,8 @@ private function load_conf() { * @since 1.8.3 */ $this->_crawler_conf['run_delay'] = 500; // microseconds - if (defined('LITESPEED_CRAWLER_USLEEP') && LITESPEED_CRAWLER_USLEEP > $this->_crawler_conf['run_delay']) { - $this->_crawler_conf['run_delay'] = LITESPEED_CRAWLER_USLEEP; + if (defined('LITESPEED_CRAWLER_USLEEP') && constant('LITESPEED_CRAWLER_USLEEP') > $this->_crawler_conf['run_delay']) { + $this->_crawler_conf['run_delay'] = constant('LITESPEED_CRAWLER_USLEEP'); } if (!empty($_SERVER[Base::ENV_CRAWLER_USLEEP]) && $_SERVER[Base::ENV_CRAWLER_USLEEP] > $this->_crawler_conf['run_delay']) { $this->_crawler_conf['run_delay'] = $_SERVER[Base::ENV_CRAWLER_USLEEP]; @@ -440,7 +441,7 @@ private function load_conf() { * @since 7.0 */ public function get_crawler_duration() { - $RUN_DURATION = defined('LITESPEED_CRAWLER_DURATION') ? LITESPEED_CRAWLER_DURATION : 900; + $RUN_DURATION = defined('LITESPEED_CRAWLER_DURATION') ? constant('LITESPEED_CRAWLER_DURATION') : 900; if ($RUN_DURATION > 900) { $RUN_DURATION = 900; // reset to default value if defined in conf file is higher than 900 seconds for security enhancement } @@ -534,7 +535,7 @@ private function _adjust_current_threads() { $curload /= $this->_ncpu; // $curload = 1; - $CRAWLER_THREADS = defined('LITESPEED_CRAWLER_THREADS') ? LITESPEED_CRAWLER_THREADS : 3; + $CRAWLER_THREADS = defined('LITESPEED_CRAWLER_THREADS') ? constant('LITESPEED_CRAWLER_THREADS') : 3; if ($this->_cur_threads == -1) { // init @@ -610,7 +611,7 @@ private function _prepare_running() { */ private function _take_over_lane() { self::debug('Take over lane as lane is free: ' . $this->json_local_path() . '.pid'); - file::save($this->json_local_path() . '.pid', LITESPEED_LANE_HASH); + File::save($this->json_local_path() . '.pid', LITESPEED_LANE_HASH); } /** @@ -651,7 +652,7 @@ private function _check_valid_lane( $strict_mode = false ) { return false; } } - $pid = file::read($lane_file); + $pid = File::read($lane_file); if ($pid && LITESPEED_LANE_HASH != $pid) { // If lane file is older than 1h, ignore if (time() - filemtime($lane_file) > 3600) { @@ -672,10 +673,11 @@ private function _check_valid_lane( $strict_mode = false ) { * @return bool true if success and can continue crawling, false if failed and need to stop */ private function _test_port() { - if (empty($this->_crawler_conf['cookies']) || empty($this->_crawler_conf['cookies']['litespeed_hash'])) { - return true; - } if (!$this->_server_ip) { + if (empty($this->_crawlers[$this->_summary['curr_crawler']]['uid'])) { + self::debug('Bypass test port as Server IP is not set'); + return true; + } self::debug('❌ Server IP not set'); return false; } @@ -697,8 +699,8 @@ private function _test_port() { $options = $this->_get_curl_options(); $home = home_url(); - File::save(LITESPEED_STATIC_DIR . '/crawler/test_port.txt', $home, true); - $url = LITESPEED_STATIC_URL . '/crawler/test_port.txt'; + File::save(LITESPEED_STATIC_DIR . '/crawler/test_port.html', $home, true); + $url = LITESPEED_STATIC_URL . '/crawler/test_port.html'; $parsed_url = parse_url($url); if (empty($parsed_url['host'])) { self::debug('❌ Test port failed, invalid URL: ' . $url); @@ -886,6 +888,21 @@ private function _do_running() { self::debug('Crawler #' . $this->_summary['curr_crawler'] . ' touched end'); } + /** + * If need to resolve DNS or not + * + * @since 7.3.0.1 + */ + private function _should_force_resolve_dns() { + if ($this->_server_ip) { + return true; + } + if (!empty($this->_crawler_conf['cookies']) && !empty($this->_crawler_conf['cookies']['litespeed_hash'])) { + return true; + } + return false; + } + /** * Send multi curl requests * If res=B, bypass request and won't return @@ -898,7 +915,7 @@ private function _multi_request( $rows, $options ) { exit('curl_multi_init disabled'); } $mh = curl_multi_init(); - $CRAWLER_DROP_DOMAIN = defined('LITESPEED_CRAWLER_DROP_DOMAIN') ? LITESPEED_CRAWLER_DROP_DOMAIN : false; + $CRAWLER_DROP_DOMAIN = defined('LITESPEED_CRAWLER_DROP_DOMAIN') ? constant('LITESPEED_CRAWLER_DROP_DOMAIN') : false; $curls = array(); foreach ($rows as $row) { if (substr($row['res'], $this->_summary['curr_crawler'], 1) == self::STATUS_BLACKLIST) { @@ -921,7 +938,7 @@ private function _multi_request( $rows, $options ) { } // IP resolve - if ((!empty($this->_crawler_conf['cookies']) && !empty($this->_crawler_conf['cookies']['litespeed_hash'])) || $this->_server_ip) { + if ($this->_should_force_resolve_dns()) { $parsed_url = parse_url($url); // self::debug('Crawl role simulator, required to use localhost for resolve'); @@ -1031,17 +1048,20 @@ private function _status_parse( $header, $code, $url ) { } // If blacklist is disabled - if ((defined('LITESPEED_CRAWLER_DISABLE_BLOCKLIST') && LITESPEED_CRAWLER_DISABLE_BLOCKLIST) || apply_filters('litespeed_crawler_disable_blocklist', false, $url)) { + if ((defined('LITESPEED_CRAWLER_DISABLE_BLOCKLIST') && constant('LITESPEED_CRAWLER_DISABLE_BLOCKLIST')) || apply_filters('litespeed_crawler_disable_blocklist', false, $url)) { return self::STATUS_MISS; } return self::STATUS_NOCACHE; // Blacklist } - $_cache_headers = array( 'x-qc-cache', 'x-lsadc-cache', 'x-litespeed-cache' ); + $_cache_headers = array( 'x-litespeed-cache', 'x-qc-cache', 'x-lsadc-cache' ); foreach ($_cache_headers as $_header) { if (stripos($header, $_header) !== false) { + if (stripos($header, $_header . ': bkn') !== false) { + return self::STATUS_HIT; // Hit + } if (stripos($header, $_header . ': miss') !== false) { return self::STATUS_MISS; // Miss } @@ -1050,7 +1070,7 @@ private function _status_parse( $header, $code, $url ) { } // If blacklist is disabled - if ((defined('LITESPEED_CRAWLER_DISABLE_BLOCKLIST') && LITESPEED_CRAWLER_DISABLE_BLOCKLIST) || apply_filters('litespeed_crawler_disable_blocklist', false, $url)) { + if ((defined('LITESPEED_CRAWLER_DISABLE_BLOCKLIST') && constant('LITESPEED_CRAWLER_DISABLE_BLOCKLIST')) || apply_filters('litespeed_crawler_disable_blocklist', false, $url)) { return self::STATUS_MISS; } @@ -1064,7 +1084,7 @@ private function _status_parse( $header, $code, $url ) { * @access private */ private function _get_curl_options( $crawler_only = false ) { - $CRAWLER_TIMEOUT = defined('LITESPEED_CRAWLER_TIMEOUT') ? LITESPEED_CRAWLER_TIMEOUT : 30; + $CRAWLER_TIMEOUT = defined('LITESPEED_CRAWLER_TIMEOUT') ? constant('LITESPEED_CRAWLER_TIMEOUT') : 30; $options = array( CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, @@ -1141,7 +1161,7 @@ public function self_curl( $url, $ua, $uid = false, $accept = false ) { if (!empty($parsed_url['host'])) { $dom = $parsed_url['host']; - $port = defined('LITESPEED_CRAWLER_LOCAL_PORT') ? LITESPEED_CRAWLER_LOCAL_PORT : '443'; + $port = defined('LITESPEED_CRAWLER_LOCAL_PORT') ? LITESPEED_CRAWLER_LOCAL_PORT : '443'; // TODO: need to test port? $resolved = $dom . ':' . $port . ':' . $this->_server_ip; $options[CURLOPT_RESOLVE] = array( $resolved ); $options[CURLOPT_DNS_USE_GLOBAL_CACHE] = false; @@ -1403,7 +1423,7 @@ public function display_status( $status_row, $reason_set ) { * * @since 1.1.0 * @access protected - * @param string $error Error info + * @param string $msg Error info */ protected function output( $msg ) { if (wp_doing_cron()) { diff --git a/src/css.cls.php b/src/css.cls.php index 5a3bcccbc..3a91a8282 100644 --- a/src/css.cls.php +++ b/src/css.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The optimize css class. diff --git a/src/data.cls.php b/src/data.cls.php index d40440f46..8bcef6fd2 100644 --- a/src/data.cls.php +++ b/src/data.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The class to store and manage litespeed db data. * * @since 1.3.1 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -18,11 +17,6 @@ class Data extends Root { const LOG_TAG = '🚀'; private $_db_updater = array( - '3.5.0.3' => array( 'litespeed_update_3_5' ), - '4.0' => array( 'litespeed_update_4' ), - '4.1' => array( 'litespeed_update_4_1' ), - '4.3' => array( 'litespeed_update_4_3' ), - '4.4.4-b1' => array( 'litespeed_update_4_4_4' ), '5.3-a5' => array( 'litespeed_update_5_3' ), '7.0-b26' => array( 'litespeed_update_7' ), '7.0.1-b1' => array( 'litespeed_update_7_0_1' ), @@ -237,62 +231,6 @@ private function _set_upgrade_lock( $lock ) { } } - /** - * Upgrade the conf to v3.0 from previous v3.0- data - * - * NOTE: Only for v3.0- - * - * @since 3.0 - * @access public - */ - public function try_upgrade_conf_3_0() { - $previous_options = get_option('litespeed-cache-conf'); - if (!$previous_options) { - return 'new'; - } - - $ver = $previous_options['version']; - - !defined('LSCWP_CUR_V') && define('LSCWP_CUR_V', $ver); - - // Init log manually - if ($this->conf(Base::O_DEBUG)) { - $this->cls('Debug2')->init(); - } - self::debug('Upgrading previous settings [from] ' . $ver . ' [to] v3.0'); - - if ($this->_get_upgrade_lock()) { - return; - } - - $this->_set_upgrade_lock(true); - - require_once LSCWP_DIR . 'src/data.upgrade.func.php'; - - // Here inside will update the version to v3.0 - litespeed_update_3_0($ver); - - $this->_set_upgrade_lock(false); - - self::debug('Upgraded to v3.0'); - - // Upgrade from 3.0 to latest version - $ver = '3.0'; - if (Core::VER != $ver) { - return $this->conf_upgrade($ver); - } else { - // Reload options - $this->cls('Conf')->load_options(); - - $this->correct_tb_existence(); - - !defined('LSWCP_EMPTYCACHE') && define('LSWCP_EMPTYCACHE', true); // clear all sites caches - Purge::purge_all(); - - return 'upgrade'; - } - } - /** * Get the table name * diff --git a/src/data.upgrade.func.php b/src/data.upgrade.func.php index 0918f11ba..64abbdda6 100644 --- a/src/data.upgrade.func.php +++ b/src/data.upgrade.func.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * Database upgrade funcs @@ -10,13 +11,10 @@ defined('WPINC') || exit(); use LiteSpeed\Debug2; -use LiteSpeed\Conf; -use LiteSpeed\Admin_Display; -use LiteSpeed\File; use LiteSpeed\Cloud; /** - * Table existance check function + * Table existence check function * * @since 7.2 */ @@ -133,642 +131,3 @@ function litespeed_update_5_3() { $wpdb->query($q); } } - -/** - * Add expired to url_file table - * - * @since 4.4.4 - */ -function litespeed_update_4_4_4() { - global $wpdb; - Debug2::debug('[Data] Upgrade url_file table'); - - $tb = $wpdb->prefix . 'litespeed_url_file'; - if (litespeed_table_exists($tb)) { - $q = - 'ALTER TABLE `' . - $tb . - '` - ADD COLUMN `expired` int(11) NOT NULL DEFAULT 0, - ADD KEY `filename_2` (`filename`,`expired`), - ADD KEY `url_id` (`url_id`,`expired`) - '; - $wpdb->query($q); - } -} - -/** - * Drop cssjs table and rm cssjs folder - * - * @since 4.3 - */ -function litespeed_update_4_3() { - if (file_exists(LITESPEED_STATIC_DIR . '/ccsjs')) { - File::rrmdir(LITESPEED_STATIC_DIR . '/ccsjs'); - } -} - -/** - * Drop object cache data file - * - * @since 4.1 - */ -function litespeed_update_4_1() { - if (file_exists(WP_CONTENT_DIR . '/.object-cache.ini')) { - unlink(WP_CONTENT_DIR . '/.object-cache.ini'); - } -} - -/** - * Drop cssjs table and rm cssjs folder - * - * @since 4.0 - */ -function litespeed_update_4() { - global $wpdb; - $tb = $wpdb->prefix . 'litespeed_cssjs'; - - if (!litespeed_table_exists($tb)) { - return; - } - - $q = 'DROP TABLE IF EXISTS ' . $tb; - $wpdb->query($q); - - if (file_exists(LITESPEED_STATIC_DIR . '/ccsjs')) { - File::rrmdir(LITESPEED_STATIC_DIR . '/ccsjs'); - } -} - -/** - * Append jQuery to JS optm exclude list for max compatibility - * Turn off JS Combine and Defer - * - * @since 3.5.1 - */ -function litespeed_update_3_5() { - $__conf = Conf::cls(); - // Excludes jQuery - foreach (array( 'optm-js_exc', 'optm-js_defer_exc' ) as $v) { - $curr_setting = $__conf->conf($v); - $curr_setting[] = 'jquery.js'; - $curr_setting[] = 'jquery.min.js'; - $__conf->update($v, $curr_setting); - } - // Turn off JS Combine and defer - $show_msg = false; - foreach (array( 'optm-js_comb', 'optm-js_defer', 'optm-js_inline_defer' ) as $v) { - $curr_setting = $__conf->conf($v); - if (!$curr_setting) { - continue; - } - $show_msg = true; - $__conf->update($v, false); - } - - if ($show_msg) { - $msg = sprintf( - __( - 'LiteSpeed Cache upgraded successfully. NOTE: Due to changes in this version, the settings %1$s and %2$s have been turned OFF. Please turn them back on manually and verify that your site layout is correct, and you have no JS errors.', - 'litespeed-cache' - ), - '<code>' . __('JS Combine', 'litespeed-cache') . '</code>', - '<code>' . __('JS Defer', 'litespeed-cache') . '</code>' - ); - $msg .= sprintf(' <a href="admin.php?page=litespeed-page_optm#settings_js">%s</a>.', __('Click here to settings', 'litespeed-cache')); - Admin_Display::info($msg, false, true); - } -} - -/** - * For version under v2.0 to v2.0+ - * - * @since 3.0 - */ -function litespeed_update_2_0( $ver ) { - global $wpdb; - - // Table version only exists after all old data migrated - // Last modified is v2.4.2 - if (version_compare($ver, '2.4.2', '<')) { - /** - * Convert old data from postmeta to img_optm table - * - * @since 2.0 - */ - - // Migrate data from `wp_postmeta` to `wp_litespeed_img_optm` - $mids_to_del = array(); - $q = "SELECT * FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_id"; - $meta_value_list = $wpdb->get_results($wpdb->prepare($q, 'litespeed-optimize-data')); - if ($meta_value_list) { - $max_k = count($meta_value_list) - 1; - foreach ($meta_value_list as $k => $v) { - $mids_to_del[] = $v->meta_id; - - // Delete from postmeta - if (count($mids_to_del) > 100 || $k == $max_k) { - $q = "DELETE FROM $wpdb->postmeta WHERE meta_id IN ( " . implode(',', array_fill(0, count($mids_to_del), '%s')) . ' ) '; - $wpdb->query($wpdb->prepare($q, $mids_to_del)); - - $mids_to_del = array(); - } - } - - Debug2::debug('[Data] img_optm inserted records: ' . $k); - } - - $q = "DELETE FROM $wpdb->postmeta WHERE meta_key = %s"; - $rows = $wpdb->query($wpdb->prepare($q, 'litespeed-optimize-status')); - Debug2::debug('[Data] img_optm delete optm_status records: ' . $rows); - } - - /** - * Add target_md5 field to table - * - * @since 2.4.2 - */ - if (version_compare($ver, '2.4.2', '<') && version_compare($ver, '2.0', '>=')) { - // NOTE: For new users, need to bypass this section - $sql = sprintf('ALTER TABLE `%1$s` ADD `server_info` text NOT NULL, DROP COLUMN `server`', $wpdb->prefix . 'litespeed_img_optm'); - - $res = $wpdb->query($sql); - if ($res !== true) { - Debug2::debug('[Data] Warning: Alter table img_optm failed!', $sql); - } else { - Debug2::debug('[Data] Successfully upgraded table img_optm.'); - } - } - - // Delete img optm tb version - delete_option($wpdb->prefix . 'litespeed_img_optm'); - - // Delete possible HTML optm data from wp_options - delete_option('litespeed-cache-optimized'); - - // Delete HTML optm tb version - delete_option($wpdb->prefix . 'litespeed_optimizer'); -} - -/** - * Move all options in litespeed-cache-conf from v3.0- to separate records - * - * @since 3.0 - */ -function litespeed_update_3_0( $ver ) { - global $wpdb; - // Upgrade v2.0- to v2.0 first - if (version_compare($ver, '2.0', '<')) { - litespeed_update_2_0($ver); - } - - set_time_limit(86400); - - // conv items to litespeed.conf.* - Debug2::debug('[Data] Conv items to litespeed.conf.*'); - $data = array( - 'litespeed-cache-exclude-cache-roles' => 'cache-exc_roles', - 'litespeed-cache-drop_qs' => 'cache-drop_qs', - 'litespeed-forced_cache_uri' => 'cache-force_uri', - 'litespeed-cache_uri_priv' => 'cache-priv_uri', - 'litespeed-excludes_uri' => 'cache-exc', - 'litespeed-cache-vary-group' => 'cache-vary_group', - 'litespeed-adv-purge_all_hooks' => 'purge-hook_all', - 'litespeed-object_global_groups' => 'object-global_groups', - 'litespeed-object_non_persistent_groups' => 'object-non_persistent_groups', - 'litespeed-media-lazy-img-excludes' => 'media-lazy_exc', - 'litespeed-media-lazy-img-cls-excludes' => 'media-lazy_cls_exc', - 'litespeed-media-webp_attribute' => 'img_optm-webp_attr', - 'litespeed-optm-css' => 'optm-ccss_con', - 'litespeed-optm_excludes' => 'optm-exc', - 'litespeed-optm-ccss-separate_posttype' => 'optm-ccss_sep_posttype', - 'litespeed-optm-css-separate_uri' => 'optm-ccss_sep_uri', - 'litespeed-optm-js-defer-excludes' => 'optm-js_defer_exc', - 'litespeed-cache-dns_prefetch' => 'optm-dns_prefetch', - 'litespeed-cache-exclude-optimization-roles' => 'optm-exc_roles', - 'litespeed-log_ignore_filters' => 'debug-log_no_filters', // depreciated - 'litespeed-log_ignore_part_filters' => 'debug-log_no_part_filters', // depreciated - 'litespeed-cdn-ori_dir' => 'cdn-ori_dir', - 'litespeed-cache-cdn_mapping' => 'cdn-mapping', - 'litespeed-crawler-as-uids' => 'crawler-roles', - 'litespeed-crawler-cookies' => 'crawler-cookies', - ); - foreach ($data as $k => $v) { - $old_data = get_option($k); - if ($old_data) { - Debug2::debug("[Data] Convert $k"); - // They must be an array - if (!is_array($old_data) && $v != 'optm-ccss_con') { - $old_data = explode("\n", $old_data); - } - - if ($v == 'crawler-cookies') { - $tmp = array(); - $i = 0; - foreach ($old_data as $k2 => $v2) { - $tmp[$i]['name'] = $k2; - $tmp[$i]['vals'] = explode("\n", $v2); - ++$i; - } - $old_data = $tmp; - } - - add_option('litespeed.conf.' . $v, $old_data); - } - Debug2::debug("[Data] Delete $k"); - delete_option($k); - } - - // conv other items - $data = array( - 'litespeed-setting-mode' => 'litespeed.setting.mode', - 'litespeed-media-need-pull' => 'litespeed.img_optm.need_pull', - 'litespeed-env-ref' => 'litespeed.env.ref', - 'litespeed-cache-cloudflare_status' => 'litespeed.cdn.cloudflare.status', - ); - foreach ($data as $k => $v) { - $old_data = get_option($k); - if ($old_data) { - add_option($v, $old_data); - } - delete_option($k); - } - - // Conv conf from litespeed-cache-conf child to litespeed.conf.* - Debug2::debug('[Data] Conv conf from litespeed-cache-conf child to litespeed.conf.*'); - $previous_options = get_option('litespeed-cache-conf'); - - $data = array( - 'radio_select' => 'cache', - 'hash' => 'hash', - 'auto_upgrade' => 'auto_upgrade', - 'news' => 'news', - 'crawler_domain_ip' => 'server_ip', - - 'esi_enabled' => 'esi', - 'esi_cached_admbar' => 'esi-cache_admbar', - 'esi_cached_commform' => 'esi-cache_commform', - - 'heartbeat' => 'misc-heartbeat_front', - - 'cache_browser' => 'cache-browser', - 'cache_browser_ttl' => 'cache-ttl_browser', - 'instant_click' => 'util-instant_click', - 'use_http_for_https_vary' => 'util-no_https_vary', - - 'purge_upgrade' => 'purge-upgrade', - 'timed_urls' => 'purge-timed_urls', - 'timed_urls_time' => 'purge-timed_urls_time', - - 'cache_priv' => 'cache-priv', - 'cache_commenter' => 'cache-commenter', - 'cache_rest' => 'cache-rest', - 'cache_page_login' => 'cache-page_login', - 'cache_favicon' => 'cache-favicon', - 'cache_resources' => 'cache-resources', - 'mobileview_enabled' => 'cache-mobile', - 'mobileview_rules' => 'cache-mobile_rules', - 'nocache_useragents' => 'cache-exc_useragents', - 'nocache_cookies' => 'cache-exc_cookies', - 'excludes_qs' => 'cache-exc_qs', - 'excludes_cat' => 'cache-exc_cat', - 'excludes_tag' => 'cache-exc_tag', - 'public_ttl' => 'cache-ttl_pub', - 'private_ttl' => 'cache-ttl_priv', - 'front_page_ttl' => 'cache-ttl_frontpage', - 'feed_ttl' => 'cache-ttl_feed', - 'login_cookie' => 'cache-login_cookie', - - 'debug_disable_all' => 'debug-disable_all', - 'debug' => 'debug', - 'admin_ips' => 'debug-ips', - 'debug_level' => 'debug-level', - 'log_file_size' => 'debug-filesize', - 'debug_cookie' => 'debug-cookie', - 'collapse_qs' => 'debug-collapse_qs', - // 'log_filters' => 'debug-log_filters', - - 'crawler_cron_active' => 'crawler', - // 'crawler_include_posts' => 'crawler-inc_posts', - // 'crawler_include_pages' => 'crawler-inc_pages', - // 'crawler_include_cats' => 'crawler-inc_cats', - // 'crawler_include_tags' => 'crawler-inc_tags', - // 'crawler_excludes_cpt' => 'crawler-exc_cpt', - // 'crawler_order_links' => 'crawler-order_links', - 'crawler_usleep' => 'crawler-usleep', - 'crawler_run_duration' => 'crawler-run_duration', - 'crawler_run_interval' => 'crawler-run_interval', - 'crawler_crawl_interval' => 'crawler-crawl_interval', - 'crawler_threads' => 'crawler-threads', - 'crawler_load_limit' => 'crawler-load_limit', - 'crawler_custom_sitemap' => 'crawler-sitemap', - - 'cache_object' => 'object', - 'cache_object_kind' => 'object-kind', - 'cache_object_host' => 'object-host', - 'cache_object_port' => 'object-port', - 'cache_object_life' => 'object-life', - 'cache_object_persistent' => 'object-persistent', - 'cache_object_admin' => 'object-admin', - 'cache_object_transients' => 'object-transients', - 'cache_object_db_id' => 'object-db_id', - 'cache_object_user' => 'object-user', - 'cache_object_pswd' => 'object-psw', - - 'cdn' => 'cdn', - 'cdn_ori' => 'cdn-ori', - 'cdn_exclude' => 'cdn-exc', - // 'cdn_remote_jquery' => 'cdn-remote_jq', - 'cdn_quic' => 'cdn-quic', - 'cdn_cloudflare' => 'cdn-cloudflare', - 'cdn_cloudflare_email' => 'cdn-cloudflare_email', - 'cdn_cloudflare_key' => 'cdn-cloudflare_key', - 'cdn_cloudflare_name' => 'cdn-cloudflare_name', - 'cdn_cloudflare_zone' => 'cdn-cloudflare_zone', - - 'media_img_lazy' => 'media-lazy', - 'media_img_lazy_placeholder' => 'media-lazy_placeholder', - 'media_placeholder_resp' => 'media-placeholder_resp', - 'media_placeholder_resp_color' => 'media-placeholder_resp_color', - 'media_placeholder_resp_async' => 'media-placeholder_resp_async', - 'media_iframe_lazy' => 'media-iframe_lazy', - // 'media_img_lazyjs_inline' => 'media-lazyjs_inline', - - 'media_optm_auto' => 'img_optm-auto', - 'media_optm_cron' => 'img_optm-cron', - 'media_optm_ori' => 'img_optm-ori', - 'media_rm_ori_bkup' => 'img_optm-rm_bkup', - // 'media_optm_webp' => 'img_optm-webp', - 'media_webp_replace' => 'img_optm-webp', - 'media_optm_lossless' => 'img_optm-lossless', - 'media_optm_exif' => 'img_optm-exif', - 'media_webp_replace_srcset' => 'img_optm-webp_replace_srcset', - - 'css_minify' => 'optm-css_min', - // 'css_inline_minify' => 'optm-css_inline_min', - 'css_combine' => 'optm-css_comb', - // 'css_combined_priority' => 'optm-css_comb_priority', - // 'css_http2' => 'optm-css_http2', - 'css_exclude' => 'optm-css_exc', - 'js_minify' => 'optm-js_min', - // 'js_inline_minify' => 'optm-js_inline_min', - 'js_combine' => 'optm-js_comb', - // 'js_combined_priority' => 'optm-js_comb_priority', - // 'js_http2' => 'optm-js_http2', - 'js_exclude' => 'optm-js_exc', - // 'optimize_ttl' => 'optm-ttl', - 'html_minify' => 'optm-html_min', - 'optm_qs_rm' => 'optm-qs_rm', - 'optm_ggfonts_rm' => 'optm-ggfonts_rm', - 'optm_css_async' => 'optm-css_async', - // 'optm_ccss_gen' => 'optm-ccss_gen', - // 'optm_ccss_async' => 'optm-ccss_async', - 'optm_css_async_inline' => 'optm-css_async_inline', - 'optm_js_defer' => 'optm-js_defer', - 'optm_emoji_rm' => 'optm-emoji_rm', - // 'optm_exclude_jquery' => 'optm-exc_jq', - 'optm_ggfonts_async' => 'optm-ggfonts_async', - // 'optm_max_size' => 'optm-max_size', - // 'optm_rm_comment' => 'optm-rm_comment', - ); - foreach ($data as $k => $v) { - if (!isset($previous_options[$k])) { - continue; - } - // The following values must be array - if (!is_array($previous_options[$k])) { - if (in_array($v, array( 'cdn-ori', 'cache-exc_cat', 'cache-exc_tag' ))) { - $previous_options[$k] = explode(',', $previous_options[$k]); - $previous_options[$k] = array_filter($previous_options[$k]); - } elseif (in_array($v, array( 'cache-mobile_rules', 'cache-exc_useragents', 'cache-exc_cookies' ))) { - $previous_options[$k] = explode('|', str_replace('\\ ', ' ', $previous_options[$k])); - $previous_options[$k] = array_filter($previous_options[$k]); - } elseif ( - in_array($v, array( - 'purge-timed_urls', - 'cache-exc_qs', - 'debug-ips', - // 'crawler-exc_cpt', - 'cdn-exc', - 'optm-css_exc', - 'optm-js_exc', - )) - ) { - $previous_options[$k] = explode("\n", $previous_options[$k]); - $previous_options[$k] = array_filter($previous_options[$k]); - } - } - - // Special handler for heartbeat - if ($v == 'misc-heartbeat_front') { - if (!$previous_options[$k]) { - add_option('litespeed.conf.misc-heartbeat_front', true); - add_option('litespeed.conf.misc-heartbeat_back', true); - add_option('litespeed.conf.misc-heartbeat_editor', true); - add_option('litespeed.conf.misc-heartbeat_front_ttl', 0); - add_option('litespeed.conf.misc-heartbeat_back_ttl', 0); - add_option('litespeed.conf.misc-heartbeat_editor_ttl', 0); - } - continue; - } - - add_option('litespeed.conf.' . $v, $previous_options[$k]); - } - // Conv purge_by_post - $data = array( - '-' => 'purge-post_all', - 'F' => 'purge-post_f', - 'H' => 'purge-post_h', - 'PGS' => 'purge-post_p', - 'PGSRP' => 'purge-post_pwrp', - 'A' => 'purge-post_a', - 'Y' => 'purge-post_y', - 'M' => 'purge-post_m', - 'D' => 'purge-post_d', - 'T' => 'purge-post_t', - 'PT' => 'purge-post_pt', - ); - if (isset($previous_options['purge_by_post'])) { - $purge_by_post = explode('.', $previous_options['purge_by_post']); - foreach ($data as $k => $v) { - add_option('litespeed.conf.' . $v, in_array($k, $purge_by_post)); - } - } - // Conv 404/403/500 TTL - $ttl_status = array(); - if (isset($previous_options['403_ttl'])) { - $ttl_status[] = '403 ' . $previous_options['403_ttl']; - } - if (isset($previous_options['404_ttl'])) { - $ttl_status[] = '404 ' . $previous_options['404_ttl']; - } - if (isset($previous_options['500_ttl'])) { - $ttl_status[] = '500 ' . $previous_options['500_ttl']; - } - add_option('litespeed.conf.cache-ttl_status', $ttl_status); - - /** - * Resave cdn cfg from lscfg to separate cfg when upgrade to v1.7 - * - * NOTE: this can be left here as `add_option` bcos it is after the item `litespeed-cache-cdn_mapping` is converted - * - * @since 1.7 - */ - if (isset($previous_options['cdn_url'])) { - $cdn_mapping = array( - 'url' => $previous_options['cdn_url'], - 'inc_img' => $previous_options['cdn_inc_img'], - 'inc_css' => $previous_options['cdn_inc_css'], - 'inc_js' => $previous_options['cdn_inc_js'], - 'filetype' => $previous_options['cdn_filetype'], - ); - add_option('litespeed.conf.cdn-mapping', array( $cdn_mapping )); - Debug2::debug('[Data] plugin_upgrade option adding CDN map'); - } - - /** - * Move Exclude settings to separate item - * - * NOTE: this can be left here as `add_option` bcos it is after the relevant items are converted - * - * @since 2.3 - */ - if (isset($previous_options['forced_cache_uri'])) { - add_option('litespeed.conf.cache-force_uri', $previous_options['forced_cache_uri']); - } - if (isset($previous_options['cache_uri_priv'])) { - add_option('litespeed.conf.cache-priv_uri', $previous_options['cache_uri_priv']); - } - if (isset($previous_options['optm_excludes'])) { - add_option('litespeed.conf.optm-exc', $previous_options['optm_excludes']); - } - if (isset($previous_options['excludes_uri'])) { - add_option('litespeed.conf.cache-exc', $previous_options['excludes_uri']); - } - - // Backup stale conf - Debug2::debug('[Data] Backup stale conf'); - delete_option('litespeed-cache-conf'); - add_option('litespeed-cache-conf.bk', $previous_options); - - // Upgrade site_options if is network - if (is_multisite()) { - $ver = get_site_option('litespeed.conf._version'); - if (!$ver) { - Debug2::debug('[Data] Conv multisite'); - $previous_site_options = get_site_option('litespeed-cache-conf'); - - $data = array( - 'network_enabled' => 'cache', - 'use_primary_settings' => 'use_primary_settings', - 'auto_upgrade' => 'auto_upgrade', - 'purge_upgrade' => 'purge-upgrade', - - 'cache_favicon' => 'cache-favicon', - 'cache_resources' => 'cache-resources', - 'mobileview_enabled' => 'cache-mobile', - 'mobileview_rules' => 'cache-mobile_rules', - 'login_cookie' => 'cache-login_cookie', - 'nocache_cookies' => 'cache-exc_cookies', - 'nocache_useragents' => 'cache-exc_useragents', - - 'cache_object' => 'object', - 'cache_object_kind' => 'object-kind', - 'cache_object_host' => 'object-host', - 'cache_object_port' => 'object-port', - 'cache_object_life' => 'object-life', - 'cache_object_persistent' => 'object-persistent', - 'cache_object_admin' => 'object-admin', - 'cache_object_transients' => 'object-transients', - 'cache_object_db_id' => 'object-db_id', - 'cache_object_user' => 'object-user', - 'cache_object_pswd' => 'object-psw', - - 'cache_browser' => 'cache-browser', - 'cache_browser_ttl' => 'cache-ttl_browser', - - 'media_webp_replace' => 'img_optm-webp', - ); - foreach ($data as $k => $v) { - if (!isset($previous_site_options[$k])) { - continue; - } - // The following values must be array - if (!is_array($previous_site_options[$k])) { - if (in_array($v, array( 'cache-mobile_rules', 'cache-exc_useragents', 'cache-exc_cookies' ))) { - $previous_site_options[$k] = explode('|', str_replace('\\ ', ' ', $previous_site_options[$k])); - $previous_site_options[$k] = array_filter($previous_site_options[$k]); - } - } - - add_site_option('litespeed.conf.' . $v, $previous_site_options[$k]); - } - - // These are already converted to single record in single site - $data = array( 'object-global_groups', 'object-non_persistent_groups' ); - foreach ($data as $v) { - $old_data = get_option($v); - if ($old_data) { - add_site_option('litespeed.conf.' . $v, $old_data); - } - } - - delete_site_option('litespeed-cache-conf'); - - add_site_option('litespeed.conf._version', '3.0'); - } - } - - // delete tables - Debug2::debug('[Data] Drop litespeed_optimizer'); - $q = 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'litespeed_optimizer'; - $wpdb->query($q); - - // Update image optm table - Debug2::debug('[Data] Upgrade img_optm table'); - - $tb = $wpdb->prefix . 'litespeed_img_optm'; - if (litespeed_table_exists($tb)) { - $status_mapping = array( - 'requested' => 3, - 'notified' => 6, - 'pulled' => 9, - 'failed' => -1, - 'miss' => -3, - 'err' => -9, - 'err_fetch' => -5, - 'err_optm' => -7, - 'xmeta' => -8, - ); - foreach ($status_mapping as $k => $v) { - $q = 'UPDATE `' . $tb . "` SET optm_status='$v' WHERE optm_status='$k'"; - $wpdb->query($q); - } - - $q = - 'ALTER TABLE `' . - $tb . - '` - DROP INDEX `post_id_2`, - DROP INDEX `root_id`, - DROP INDEX `src_md5`, - DROP INDEX `srcpath_md5`, - DROP COLUMN `srcpath_md5`, - DROP COLUMN `src_md5`, - DROP COLUMN `root_id`, - DROP COLUMN `target_saved`, - DROP COLUMN `webp_saved`, - DROP COLUMN `server_info`, - MODIFY COLUMN `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - MODIFY COLUMN `optm_status` tinyint(4) NOT NULL DEFAULT 0, - MODIFY COLUMN `src` text COLLATE utf8mb4_unicode_ci NOT NULL - '; - $wpdb->query($q); - } - - delete_option('litespeed-recommended'); - - Debug2::debug('[Data] litespeed_update_3_0 done!'); - - add_option('litespeed.conf._version', '3.0'); -} diff --git a/src/db-optm.cls.php b/src/db-optm.cls.php index e94154794..893a98b5d 100644 --- a/src/db-optm.cls.php +++ b/src/db-optm.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The admin optimize tool * * @since 1.2.1 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -251,7 +250,7 @@ private function _db_clean( $type ) { */ public function list_myisam() { global $wpdb; - $q = "SELECT * FROM information_schema.tables WHERE TABLE_SCHEMA = '" . DB_NAME . "' and ENGINE = 'myisam' AND TABLE_NAME LIKE '{$wpdb->prefix}%'"; + $q = "SELECT TABLE_NAME as table_name, ENGINE as engine FROM information_schema.tables WHERE TABLE_SCHEMA = '" . DB_NAME . "' and ENGINE = 'myisam' AND TABLE_NAME LIKE '{$wpdb->prefix}%'"; return $wpdb->get_results($q); } @@ -273,8 +272,8 @@ private function _conv_innodb() { $list = $this->list_myisam(); foreach ($list as $v) { - if ($v->TABLE_NAME == $_GET['tb']) { - $tb = $v->TABLE_NAME; + if ($v->table_name == $_GET['tb']) { + $tb = $v->table_name; break; } } @@ -351,4 +350,19 @@ public function handler() { Admin::redirect(); } + + /** + * Clean DB + * + * @since 7.0 + * @access public + */ + public function handler_clean_db_cli($args) + { + if (defined('WP_CLI') && constant('WP_CLI')) { + return $this->_db_clean($args); + } + + return false; + } } diff --git a/src/debug2.cls.php b/src/debug2.cls.php index c75a067e5..4f264c6a8 100644 --- a/src/debug2.cls.php +++ b/src/debug2.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The plugin logging class. @@ -48,6 +49,49 @@ public function __construct() { defined('LSCWP_DEBUG_EXC_STRINGS') || define('LSCWP_DEBUG_EXC_STRINGS', $this->conf(Base::O_DEBUG_EXC_STRINGS)); } + /** + * Disable all functionalities for a time period. + * + * @since 7.4 + * @access public + * @param integer $time How long should we disable LSC functionalities. + */ + public static function tmp_disable( $time = 86400 ) { + $conf = Conf::cls(); + $disabled = self::cls()->conf( Base::DEBUG_TMP_DISABLE ); + + if ( 0 === $disabled ) { + $conf->update_confs( array( Base::DEBUG_TMP_DISABLE => time() + $time ) ); + self::debug2( 'LiteSpeed Cache temporary disabled.' ); + + return; + } + + $conf->update_confs( array( Base::DEBUG_TMP_DISABLE => 0 ) ); + self::debug2( 'LiteSpeed Cache reactivated.' ); + } + + /** + * Test if Disable All is active. Disable if time is reached. + * + * @since 7.4 + * @access public + */ + public static function is_tmp_disable() { + $disabled_time = self::cls()->conf( Base::DEBUG_TMP_DISABLE ); + + if ( 0 === $disabled_time ) { + return false; + } + + if ( time() - $disabled_time < 0 ){ + return true; + } + + Conf::cls()->update_confs( array( Base::DEBUG_TMP_DISABLE => 0 ) ); + return false; + } + /** * Try moving legacy logs into /litespeed/debug/ folder * @@ -57,7 +101,7 @@ private function _maybe_init_folder() { if (file_exists(self::$log_path_prefix . 'index.php')) { return; } - file::save(self::$log_path_prefix . 'index.php', '<?php // Silence is golden.', true); + File::save(self::$log_path_prefix . 'index.php', '<?php // Silence is golden.', true); $logs = array( 'debug', 'debug.purge', 'crawler' ); foreach ($logs as $log) { @@ -471,7 +515,7 @@ private static function push( $msg, $backtrace_limit = false ) { private static function _backtrace_info( $backtrace_limit ) { $msg = ''; - $trace = version_compare(PHP_VERSION, '5.4.0', '<') ? debug_backtrace() : debug_backtrace(false, $backtrace_limit + 3); + $trace = debug_backtrace(false, $backtrace_limit + 3); for ($i = 2; $i <= $backtrace_limit + 2; $i++) { // 0st => _backtrace_info(), 1st => push() if (empty($trace[$i]['class'])) { diff --git a/src/doc.cls.php b/src/doc.cls.php index 3d61bb4af..39a2f76a5 100644 --- a/src/doc.cls.php +++ b/src/doc.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The Doc class. * * @since 2.2.7 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -142,20 +141,6 @@ public static function notice_htaccess() { echo '</font>'; } - /** - * Notice for whitelist IPs - * - * @since 3.0 - * @access public - */ - public static function notice_ips() { - echo '<div class="litespeed-primary">'; - echo '⚠️ ' . sprintf(__('For online services to work correctly, you must allowlist all %s server IPs.', 'litespeed-cache'), 'QUIC.cloud') . '<br/>'; - echo '     ' . __('Before generating key, please verify all IPs on this list are allowlisted', 'litespeed-cache') . ': '; - echo '<a href="' . Cloud::CLOUD_IPS . '" target="_blank">' . __('Current Online Server IPs', 'litespeed-cache') . '</a>'; - echo '</div>'; - } - /** * Gentle reminder that web services run asynchronously * diff --git a/src/error.cls.php b/src/error.cls.php index 8b44b1bf7..83ce28f30 100644 --- a/src/error.cls.php +++ b/src/error.cls.php @@ -1,231 +1,251 @@ <?php - +// phpcs:ignoreFile /** * The error class. * * @since 3.0 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Error + * + * Handles error message translation and throwing for LiteSpeed Cache. + * + * @since 3.0 + */ class Error { + /** + * Error code mappings to numeric values. + * + * @since 3.0 + * @var array + */ private static $CODE_SET = array( 'HTA_LOGIN_COOKIE_INVALID' => 4300, // .htaccess did not find. - 'HTA_DNF' => 4500, // .htaccess did not find. - 'HTA_BK' => 9010, // backup - 'HTA_R' => 9041, // read htaccess - 'HTA_W' => 9042, // write - 'HTA_GET' => 9030, // failed to get + 'HTA_DNF' => 4500, // .htaccess did not find. + 'HTA_BK' => 9010, // backup + 'HTA_R' => 9041, // read htaccess + 'HTA_W' => 9042, // write + 'HTA_GET' => 9030, // failed to get ); /** - * Throw an error with msg + * Throw an error with message + * + * Throws an exception with the translated error message. * * @since 3.0 + * @access public + * @param string $code Error code. + * @param mixed $args Optional arguments for message formatting. + * @throws \Exception Always throws an exception with the error message. */ public static function t( $code, $args = null ) { - throw new \Exception(self::msg($code, $args)); + throw new \Exception( wp_kses_post( self::msg( $code, $args ) ) ); } /** * Translate an error to description * + * Converts error codes to human-readable messages. + * * @since 3.0 + * @access public + * @param string $code Error code. + * @param mixed $args Optional arguments for message formatting. + * @return string Translated error message. */ public static function msg( $code, $args = null ) { - switch ($code) { - case 'disabled_all': - $msg = - sprintf(__('The setting %s is currently enabled.', 'litespeed-cache'), '<strong>' . Lang::title(Base::O_DEBUG_DISABLE_ALL) . '</strong>') . - Doc::learn_more( - is_network_admin() ? network_admin_url('admin.php?page=litespeed-toolbox#settings-debug') : admin_url('admin.php?page=litespeed-toolbox#settings-debug'), - __('Click here to change.', 'litespeed-cache'), - true, - false, - true - ); - break; - + switch ( $code ) { case 'qc_setup_required': - $msg = - sprintf(__('You will need to finish %s setup to use the online services.', 'litespeed-cache'), '<strong>QUIC.cloud</strong>') . - Doc::learn_more(admin_url('admin.php?page=litespeed-general'), __('Click here to set.', 'litespeed-cache'), true, false, true); + $msg = + sprintf( + __( 'You will need to finish %s setup to use the online services.', 'litespeed-cache' ), + '<strong>QUIC.cloud</strong>' + ) . + Doc::learn_more( + admin_url( 'admin.php?page=litespeed-general' ), + __( 'Click here to set.', 'litespeed-cache' ), + true, + false, + true + ); break; case 'out_of_daily_quota': - $msg = __('You have used all of your daily quota for today.', 'litespeed-cache'); - $msg .= - ' ' . - Doc::learn_more( - 'https://docs.quic.cloud/billing/services/#daily-limits-on-free-quota-usage', - __('Learn more or purchase additional quota.', 'litespeed-cache'), - false, - false, - true - ); + $msg = __( 'You have used all of your daily quota for today.', 'litespeed-cache' ); + $msg .= + ' ' . + Doc::learn_more( + 'https://docs.quic.cloud/billing/services/#daily-limits-on-free-quota-usage', + __( 'Learn more or purchase additional quota.', 'litespeed-cache' ), + false, + false, + true + ); break; case 'out_of_quota': - $msg = __('You have used all of your quota left for current service this month.', 'litespeed-cache'); - $msg .= - ' ' . - Doc::learn_more( - 'https://docs.quic.cloud/billing/services/#daily-limits-on-free-quota-usage', - __('Learn more or purchase additional quota.', 'litespeed-cache'), - false, - false, - true - ); + $msg = __( 'You have used all of your quota left for current service this month.', 'litespeed-cache' ); + $msg .= + ' ' . + Doc::learn_more( + 'https://docs.quic.cloud/billing/services/#daily-limits-on-free-quota-usage', + __( 'Learn more or purchase additional quota.', 'litespeed-cache' ), + false, + false, + true + ); break; case 'too_many_requested': - $msg = __('You have too many requested images, please try again in a few minutes.', 'litespeed-cache'); + $msg = __( 'You have too many requested images, please try again in a few minutes.', 'litespeed-cache' ); break; case 'too_many_notified': - $msg = __('You have images waiting to be pulled. Please wait for the automatic pull to complete, or pull them down manually now.', 'litespeed-cache'); + $msg = __( 'You have images waiting to be pulled. Please wait for the automatic pull to complete, or pull them down manually now.', 'litespeed-cache' ); break; case 'empty_list': - $msg = __('The image list is empty.', 'litespeed-cache'); + $msg = __( 'The image list is empty.', 'litespeed-cache' ); break; case 'lack_of_param': - $msg = __('Not enough parameters. Please check if the domain key is set correctly', 'litespeed-cache'); + $msg = __( 'Not enough parameters. Please check if the QUIC.cloud connection is set correctly', 'litespeed-cache' ); break; case 'unfinished_queue': - $msg = __('There is proceeding queue not pulled yet.', 'litespeed-cache'); + $msg = __( 'There is proceeding queue not pulled yet.', 'litespeed-cache' ); break; - case strpos($code, 'unfinished_queue ') === 0: - $msg = sprintf( - __('There is proceeding queue not pulled yet. Queue info: %s.', 'litespeed-cache'), - '<code>' . substr($code, strlen('unfinished_queue ')) . '</code>' - ); + case 0 === strpos( $code, 'unfinished_queue ' ): + $msg = sprintf( + __( 'There is proceeding queue not pulled yet. Queue info: %s.', 'litespeed-cache' ), + '<code>' . substr( $code, strlen( 'unfinished_queue ' ) ) . '</code>' + ); break; case 'err_alias': - $msg = __('The site is not a valid alias on QUIC.cloud.', 'litespeed-cache'); + $msg = __( 'The site is not a valid alias on QUIC.cloud.', 'litespeed-cache' ); break; case 'site_not_registered': - $msg = __('The site is not registered on QUIC.cloud.', 'litespeed-cache'); + $msg = __( 'The site is not registered on QUIC.cloud.', 'litespeed-cache' ); break; case 'err_key': - $msg = __('The domain key is not correct. Please try to sync your domain key again.', 'litespeed-cache'); + $msg = __( 'The QUIC.cloud connection is not correct. Please try to sync your QUIC.cloud connection again.', 'litespeed-cache' ); break; case 'heavy_load': - $msg = __('The current server is under heavy load.', 'litespeed-cache'); + $msg = __( 'The current server is under heavy load.', 'litespeed-cache' ); break; case 'redetect_node': - $msg = __('Online node needs to be redetected.', 'litespeed-cache'); + $msg = __( 'Online node needs to be redetected.', 'litespeed-cache' ); break; case 'err_overdraw': - $msg = __('Credits are not enough to proceed the current request.', 'litespeed-cache'); + $msg = __( 'Credits are not enough to proceed the current request.', 'litespeed-cache' ); break; case 'W': - $msg = __('%s file not writable.', 'litespeed-cache'); + $msg = __( '%s file not writable.', 'litespeed-cache' ); break; case 'HTA_DNF': - if (!is_array($args)) { + if ( ! is_array( $args ) ) { $args = array( '<code>' . $args . '</code>' ); } - $args[] = '.htaccess'; - $msg = __('Could not find %1$s in %2$s.', 'litespeed-cache'); + $args[] = '.htaccess'; + $msg = __( 'Could not find %1$s in %2$s.', 'litespeed-cache' ); break; case 'HTA_LOGIN_COOKIE_INVALID': - $msg = sprintf(__('Invalid login cookie. Please check the %s file.', 'litespeed-cache'), '.htaccess'); + $msg = sprintf( __( 'Invalid login cookie. Please check the %s file.', 'litespeed-cache' ), '.htaccess' ); break; case 'HTA_BK': - $msg = sprintf(__('Failed to back up %s file, aborted changes.', 'litespeed-cache'), '.htaccess'); + $msg = sprintf( __( 'Failed to back up %s file, aborted changes.', 'litespeed-cache' ), '.htaccess' ); break; case 'HTA_R': - $msg = sprintf(__('%s file not readable.', 'litespeed-cache'), '.htaccess'); + $msg = sprintf( __( '%s file not readable.', 'litespeed-cache' ), '.htaccess' ); break; case 'HTA_W': - $msg = sprintf(__('%s file not writable.', 'litespeed-cache'), '.htaccess'); + $msg = sprintf( __( '%s file not writable.', 'litespeed-cache' ), '.htaccess' ); break; case 'HTA_GET': - $msg = sprintf(__('Failed to get %s file contents.', 'litespeed-cache'), '.htaccess'); + $msg = sprintf( __( 'Failed to get %s file contents.', 'litespeed-cache' ), '.htaccess' ); break; case 'failed_tb_creation': - $msg = __('Failed to create table %1$s! SQL: %2$s.', 'litespeed-cache'); + $msg = __( 'Failed to create table %1$s! SQL: %2$s.', 'litespeed-cache' ); break; case 'crawler_disabled': - $msg = __('Crawler disabled by the server admin.', 'litespeed-cache'); + $msg = __( 'Crawler disabled by the server admin.', 'litespeed-cache' ); break; case 'try_later': // QC error code - $msg = __('Previous request too recent. Please try again later.', 'litespeed-cache'); + $msg = __( 'Previous request too recent. Please try again later.', 'litespeed-cache' ); break; - case strpos($code, 'try_later ') === 0: - $msg = sprintf( - __('Previous request too recent. Please try again after %s.', 'litespeed-cache'), - '<code>' . Utility::readable_time(substr($code, strlen('try_later ')), 3600, true) . '</code>' - ); + case 0 === strpos( $code, 'try_later ' ): + $msg = sprintf( + __( 'Previous request too recent. Please try again after %s.', 'litespeed-cache' ), + '<code>' . Utility::readable_time( substr( $code, strlen( 'try_later ' ) ), 3600, true ) . '</code>' + ); break; case 'waiting_for_approval': - $msg = __('Your application is waiting for approval.', 'litespeed-cache'); + $msg = __( 'Your application is waiting for approval.', 'litespeed-cache' ); break; case 'callback_fail_hash': - $msg = __('The callback validation to your domain failed due to hash mismatch.', 'litespeed-cache'); + $msg = __( 'The callback validation to your domain failed due to hash mismatch.', 'litespeed-cache' ); break; case 'callback_fail': - $msg = __('The callback validation to your domain failed. Please make sure there is no firewall blocking our servers.', 'litespeed-cache'); + $msg = __( 'The callback validation to your domain failed. Please make sure there is no firewall blocking our servers.', 'litespeed-cache' ); break; - case substr($code, 0, 14) === 'callback_fail ': - $msg = - __('The callback validation to your domain failed. Please make sure there is no firewall blocking our servers. Response code: ', 'litespeed-cache') . - substr($code, 14); + case substr( $code, 0, 14 ) === 'callback_fail ': + $msg = + __( 'The callback validation to your domain failed. Please make sure there is no firewall blocking our servers. Response code: ', 'litespeed-cache' ) . + substr( $code, 14 ); break; case 'forbidden': - $msg = __('Your domain has been forbidden from using our services due to a previous policy violation.', 'litespeed-cache'); + $msg = __( 'Your domain has been forbidden from using our services due to a previous policy violation.', 'litespeed-cache' ); break; case 'err_dns_active': - $msg = __( - 'You cannot remove this DNS zone, because it is still in use. Please update the domain\'s nameservers, then try to delete this zone again, otherwise your site will become inaccessible.', - 'litespeed-cache' - ); + $msg = __( + 'You cannot remove this DNS zone, because it is still in use. Please update the domain\'s nameservers, then try to delete this zone again, otherwise your site will become inaccessible.', + 'litespeed-cache' + ); break; default: - $msg = __('Unknown error', 'litespeed-cache') . ': ' . $code; + $msg = __( 'Unknown error', 'litespeed-cache' ) . ': ' . $code; break; } - if ($args !== null) { - $msg = is_array($args) ? vsprintf($msg, $args) : sprintf($msg, $args); + if ( null !== $args ) { + $msg = is_array( $args ) ? vsprintf( $msg, $args ) : sprintf( $msg, $args ); } - if (isset(self::$CODE_SET[$code])) { - $msg = 'ERROR ' . self::$CODE_SET[$code] . ': ' . $msg; + if ( isset( self::$CODE_SET[ $code ] ) ) { + $msg = 'ERROR ' . self::$CODE_SET[ $code ] . ': ' . $msg; } return $msg; diff --git a/src/esi.cls.php b/src/esi.cls.php index 3609df0d1..954ad7680 100644 --- a/src/esi.cls.php +++ b/src/esi.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The ESI class. @@ -7,8 +8,6 @@ * * @since 1.1.3 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -442,14 +441,14 @@ private static function _build_inline( $url, $inline_param ) { * * @since 1.1.3 * @access private - * @param string $block_id The id to use to display the correct esi block. - * @param string $wrapper The wrapper for the esi comments. - * @param array $params The esi parameters. - * @param string $control The cache control attribute if any. - * @param bool $silence If generate wrapper comment or not - * @param bool $preserved If this ESI block is used in any filter, need to temporarily convert it to a string to avoid the HTML tag being removed/filtered. - * @param bool $svar If store the value in memory or not, in memory will be faster - * @param array $inline_val If show the current value for current request( this can avoid multiple esi requests in first time cache generating process ) + * @param string $block_id The id to use to display the correct esi block. + * @param string $wrapper The wrapper for the esi comments. + * @param array $params The esi parameters. + * @param string $control The cache control attribute if any. + * @param bool $silence If generate wrapper comment or not + * @param bool $preserved If this ESI block is used in any filter, need to temporarily convert it to a string to avoid the HTML tag being removed/filtered. + * @param bool $svar If store the value in memory or not, in memory will be faster + * @param array $inline_param If show the current value for current request( this can avoid multiple esi requests in first time cache generating process ) */ public function sub_esi_block( $block_id, @@ -706,9 +705,9 @@ public static function widget_default_options( $options, $widget ) { * * @since 1.1.3 * @access public - * @param array $instance Parameter used to build the widget. - * @param WP_Widget $widget The widget to build. - * @param array $args Parameter used to build the widget. + * @param array $instance Parameter used to build the widget. + * @param \WP_Widget $widget The widget to build. + * @param array $args Parameter used to build the widget. * @return mixed Return false if display through esi, instance otherwise. */ public function sub_widget_block( $instance, $widget, $args ) { diff --git a/src/file.cls.php b/src/file.cls.php index 145a8b20f..1945aa17a 100644 --- a/src/file.cls.php +++ b/src/file.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * LiteSpeed File Operator Library Class @@ -212,10 +213,10 @@ public static function remove_zero_space( $content ) { * Replaces existing marked info. Retains surrounding * data. Creates file if none exists. * - * @param string $filename Filename to alter. - * @param string $marker The marker to alter. - * @param array|string $insertion The new content to insert. - * @param bool $prepend Prepend insertion if not exist. + * @param string $filename Filename to alter. + * @param string $marker The marker to alter. + * @param array|string|false $insertion The new content to insert. + * @param bool $prepend Prepend insertion if not exist. * @return bool True on write success, false on failure. */ public static function insert_with_markers( $filename, $insertion = false, $marker = false, $prepend = false ) { diff --git a/src/gui.cls.php b/src/gui.cls.php index 62b608c04..88f4b5926 100644 --- a/src/gui.cls.php +++ b/src/gui.cls.php @@ -1,11 +1,11 @@ <?php +// phpcs:ignoreFile /** * The frontend GUI class. * * @since 1.3 * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -112,7 +112,7 @@ public static function pie( $percent, $width = 50, $finished_tick = false, $with $percentage = '<text x="50%" y="50%">' . $percent . ($without_percentage ? '' : '%') . '</text>'; if ($percent == 100 && $finished_tick) { - $percentage = '<text x="50%" y="50%" class="litespeed-pie-done">✓</text>'; + $percentage = '<text x="50%" y="50%" class="litespeed-pie-done">✓</text>'; } return " @@ -248,7 +248,7 @@ public static function dismiss() { break; case self::TYPE_DISMISS_PIN: - admin_display::dismiss_pin(); + Admin_display::dismiss_pin(); break; case self::TYPE_DISMISS_PROMO: @@ -368,10 +368,6 @@ public function show_promo( $check_only = false ) { // Bypass showing info banner if disabled all in debug if (defined('LITESPEED_DISABLE_ALL') && LITESPEED_DISABLE_ALL) { - if ($is_litespeed_page && !$check_only) { - include_once LSCWP_DIR . 'tpl/inc/disabled_all.php'; - } - return false; } diff --git a/src/health.cls.php b/src/health.cls.php index cc0466360..6f53c4c2c 100644 --- a/src/health.cls.php +++ b/src/health.cls.php @@ -1,11 +1,10 @@ <?php +// phpcs:ignoreFile /** * The page health * * @since 3.0 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; diff --git a/src/htaccess.cls.php b/src/htaccess.cls.php index b68c438eb..aa4b69219 100644 --- a/src/htaccess.cls.php +++ b/src/htaccess.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The htaccess rewrite rule operation class * * @since 1.0.0 * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -51,8 +50,6 @@ class Htaccess extends Root { const MARKER_START = ' start ###'; const MARKER_END = ' end ###'; - const RW_PATTERN_RES = '/.*/[^/]*(responsive|css|js|dynamic|loader|fonts)\.php'; - /** * Initialize the class and set its properties. * @@ -63,11 +60,11 @@ public function __construct() { $this->_default_frontend_htaccess = $this->frontend_htaccess; $this->_default_backend_htaccess = $this->backend_htaccess; - $frontend_htaccess = defined('LITESPEED_CFG_HTACCESS') ? LITESPEED_CFG_HTACCESS : false; + $frontend_htaccess = defined('LITESPEED_CFG_HTACCESS') ? constant('LITESPEED_CFG_HTACCESS') : false; if ($frontend_htaccess && substr($frontend_htaccess, -10) === '/.htaccess') { $this->frontend_htaccess = $frontend_htaccess; } - $backend_htaccess = defined('LITESPEED_CFG_HTACCESS_BACKEND') ? LITESPEED_CFG_HTACCESS_BACKEND : false; + $backend_htaccess = defined('LITESPEED_CFG_HTACCESS_BACKEND') ? constant('LITESPEED_CFG_HTACCESS_BACKEND') : false; if ($backend_htaccess && substr($backend_htaccess, -10) === '/.htaccess') { $this->backend_htaccess = $backend_htaccess; } @@ -572,8 +569,8 @@ private function _generate_rules( $cfg ) { // Check for WebP support via HTTP_ACCEPT $new_rules[] = 'RewriteCond %{HTTP_ACCEPT} image/' . $next_gen_format . ' [OR]'; - // Check for iPhone Safari (version > 13) - $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} iPhone.*Version/(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}).*Safari [OR]'; + // Check for iPhone browsers (version > 13) + $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} iPhone\ OS\ (1[4-9]|[2-9][0-9]) [OR]'; // Check for Firefox (version >= 65) $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} Firefox/([6-9][0-9]|[1-9][0-9]{2,})'; diff --git a/src/img-optm.cls.php b/src/img-optm.cls.php index 2a2d06335..d925eb30c 100644 --- a/src/img-optm.cls.php +++ b/src/img-optm.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The class to optimize image. * * @since 2.0 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -59,13 +58,14 @@ class Img_Optm extends Base { private $tmp_pid; private $tmp_type; private $tmp_path; - private $_img_in_queue = array(); - private $_existed_src_list = array(); - private $_pids_set = array(); + private $_img_in_queue = []; + private $_existed_src_list = []; + private $_pids_set = []; private $_thumbnail_set = ''; private $_table_img_optm; private $_table_img_optming; private $_cron_ran = false; + private $_sizes_skipped = []; private $__media; private $__data; @@ -96,6 +96,9 @@ public function __construct() { $this->_format = 'avif'; } } + + // Allow users to ignore custom sizes. + $this->_sizes_skipped = apply_filters( 'litespeed_imgoptm_sizes_skipped', $this->conf( Base::O_IMG_OPTM_SIZES_SKIPPED ) ); } /** @@ -140,7 +143,9 @@ public function wp_update_attachment_metadata( $meta_value, $post_id ) { $this->tmp_path = pathinfo($meta_value['file'], PATHINFO_DIRNAME) . '/'; $this->_append_img_queue($meta_value, true); if (!empty($meta_value['sizes'])) { - array_map(array( $this, '_append_img_queue' ), $meta_value['sizes']); + foreach( $meta_value['sizes'] as $img_size_name => $img_size ){ + $this->_append_img_queue($img_size, false, $img_size_name ); + } } if (!$this->_img_in_queue) { @@ -269,7 +274,7 @@ public function new_req() { // $allowance -= $total_new; // May need to get more images - $list = array(); + $list = []; $more = $allowance - $total_new; if ($more > 0) { $q = "SELECT b.post_id, b.meta_value @@ -306,7 +311,9 @@ public function new_req() { $this->tmp_path = pathinfo($meta_value['file'], PATHINFO_DIRNAME) . '/'; $this->_append_img_queue($meta_value, true); if (!empty($meta_value['sizes'])) { - array_map(array( $this, '_append_img_queue' ), $meta_value['sizes']); + foreach( $meta_value['sizes'] as $img_size_name => $img_size ){ + $this->_append_img_queue($img_size, false, $img_size_name ); + } } } @@ -356,9 +363,10 @@ private function _finished_running() { * Add a new img to queue which will be pushed to request * * @since 1.6 + * @since 7.5 Allow to choose which image sizes should be optimized + added parameter $img_size_name. * @access private */ - private function _append_img_queue( $meta_value, $is_ori_file = false ) { + private function _append_img_queue( $meta_value, $is_ori_file = false, $img_size_name = false ) { if (empty($meta_value['file']) || empty($meta_value['width']) || empty($meta_value['height'])) { self::debug2('bypass image due to lack of file/w/h: pid ' . $this->tmp_pid, $meta_value); return; @@ -366,8 +374,14 @@ private function _append_img_queue( $meta_value, $is_ori_file = false ) { $short_file_path = $meta_value['file']; + // Test if need to skip image size. if (!$is_ori_file) { $short_file_path = $this->tmp_path . $short_file_path; + $skip = false !== array_search( $img_size_name, $this->_sizes_skipped, true ); + if($skip){ + self::debug2( 'bypass image ' . $short_file_path . ' due to skipped size: ' . $img_size_name ); + return; + } } // Check if src is gathered already or not @@ -427,8 +441,8 @@ private function _save_raw() { if (empty($this->_img_in_queue)) { return; } - $data = array(); - $pid_list = array(); + $data = []; + $pid_list = []; foreach ($this->_img_in_queue as $k => $v) { $_img_info = $this->__media->info($v['src'], $v['pid']); @@ -457,7 +471,7 @@ private function _save_raw() { $count = count($this->_img_in_queue); self::debug('Added raw images [total] ' . $count); - $this->_img_in_queue = array(); + $this->_img_in_queue = []; // Save thumbnail groups for future rescan index $this->_gen_thumbnail_set(); @@ -468,7 +482,7 @@ private function _save_raw() { $this->_pids_set = array_merge($this->_pids_set, $pid_list); $existed_meta = $wpdb->get_results("SELECT * FROM `$wpdb->postmeta` WHERE post_id IN ('" . implode("','", $pid_list) . "') AND meta_key='" . self::DB_SET . "'"); - $existed_pid = array(); + $existed_pid = []; if ($existed_meta) { foreach ($existed_meta as $v) { $existed_pid[] = $v->post_id; @@ -503,7 +517,7 @@ private function _gen_thumbnail_set() { if ($this->_thumbnail_set) { return; } - $set = array(); + $set = []; foreach (Media::cls()->get_image_sizes() as $size) { $curr_size = $size['width'] . 'x' . $size['height']; if (in_array($curr_size, $set)) { @@ -523,7 +537,7 @@ private function _gen_thumbnail_set() { private function _filter_duplicated_src() { global $wpdb; - $srcpath_list = array(); + $srcpath_list = []; $list = $wpdb->get_results("SELECT src FROM `$this->_table_img_optming`"); foreach ($list as $v) { @@ -556,7 +570,7 @@ private function _filter_legacy_src() { return; } - $finished_ids = array(); + $finished_ids = []; Utility::compatibility(); $post_ids = array_unique(array_column($this->_img_in_queue, 'pid')); @@ -584,7 +598,7 @@ private function _filter_legacy_src() { * @access private */ private function _filter_invalid_src() { - $img_in_queue_invalid = array(); + $img_in_queue_invalid = []; foreach ($this->_img_in_queue as $k => $v) { if ($v['src']) { $extension = pathinfo($v['src'], PATHINFO_EXTENSION); @@ -625,7 +639,7 @@ private function _send_request( $allowance ) { self::debug('Load img in queue [total] ' . count($_img_in_queue)); - $list = array(); + $list = []; foreach ($_img_in_queue as $v) { $_img_info = $this->__media->info($v->src, $v->post_id); // If record is invalid, remove from img_optming table @@ -754,7 +768,7 @@ public function notify_img() { implode(',', array_fill(0, count($notified_data), '%d')) . ' )'; $list = $wpdb->get_results($wpdb->prepare($q, array_merge(array( self::DB_SIZE ), array_keys($notified_data)))); - $ls_optm_size_row_exists_postids = array(); + $ls_optm_size_row_exists_postids = []; foreach ($list as $v) { $json = $notified_data[$v->id]; // self::debug('Notified data for [id] ' . $v->id, $json); @@ -903,7 +917,7 @@ public static function async_handler( $force = false ) { return; } - if (defined('LITESPEED_IMG_OPTM_PULL_CRON') && !LITESPEED_IMG_OPTM_PULL_CRON) { + if (defined('LITESPEED_IMG_OPTM_PULL_CRON') && !constant('LITESPEED_IMG_OPTM_PULL_CRON')) { self::debug('Cron disabled [define] LITESPEED_IMG_OPTM_PULL_CRON'); return; } @@ -921,7 +935,7 @@ private function _calc_pull_threads() { global $wpdb; if (defined('LITESPEED_IMG_OPTM_PULL_THREADS')) { - return LITESPEED_IMG_OPTM_PULL_THREADS; + return constant('LITESPEED_IMG_OPTM_PULL_THREADS'); } // Tune number of images per request based on number of images waiting and cloud packages @@ -978,7 +992,7 @@ public function pull( $manual = false ) { $total_pulled_webp = 0; $total_pulled_avif = 0; - $server_list = array(); + $server_list = []; try { while ($img_rows = $wpdb->get_results($_q)) { @@ -1002,8 +1016,8 @@ public function pull( $manual = false ) { $this->_update_cron_running(); // Run requests in parallel - $requests = array(); // store each request URL for Requests::request_multiple() - $imgs_by_req = array(); // store original request data so that we can reference it in the response + $requests = []; // store each request URL for Requests::request_multiple() + $imgs_by_req = []; // store original request data so that we can reference it in the response $req_counter = 0; foreach ($img_rows as $row_img) { // request original image @@ -1065,41 +1079,14 @@ public function pull( $manual = false ) { $local_file = $this->wp_upload_dir['basedir'] . '/' . $row_img->src; $server_info = \json_decode($row_img->server_info, true); - if (empty($response->success)) { - if (!empty($response->status_code) && 404 == $response->status_code) { - $this->_step_back_image($row_img->id); - - $msg = __('Some optimized image file(s) has expired and was cleared.', 'litespeed-cache'); - Admin_Display::error($msg); - return; - } else { - // handle error - $image_url = $server_info['server'] . '/' . $server_info[$row_type]; - self::debug( - '❌ failed to pull image (' . - $row_type . - '): ' . - (!empty($response->status_code) ? $response->status_code : '') . - ' [Local: ' . - $row_img->src . - '] / [remote: ' . - $image_url . - ']' - ); - throw new \Exception('Failed to pull image ' . (!empty($response->status_code) ? $response->status_code : '') . ' [url] ' . $image_url); - return; - } - } - // Handle wp_remote_get 404 as its success=true - if (!empty($response->status_code)) { - if ($response->status_code == 404) { - $this->_step_back_image($row_img->id); + // Handle status_code 404/5xx too as its success=true + if ( empty( $response->success ) || empty( $response->status_code ) || 200 !== $response->status_code ) { + self::debug('❌ Failed to pull optimized img: HTTP error [status_code] ' . ( empty( $response->status_code ) ? 'N/A' : $response->status_code )); + $this->_step_back_image($row_img->id); - $msg = __('Some optimized image file(s) has expired and was cleared.', 'litespeed-cache'); - Admin_Display::error($msg); - return; - } - // Note: if there is other error status code found in future, handle here + $msg = __('Some optimized image file(s) has expired and was cleared.', 'litespeed-cache'); + Admin_Display::error($msg); + return; } if ('webp' === $row_type) { @@ -1210,7 +1197,7 @@ public function pull( $manual = false ) { // Save server_list to notify taken if (empty($server_list[$server_info['server']])) { - $server_list[$server_info['server']] = array(); + $server_list[$server_info['server']] = []; } $server_info_id = !empty($server_info['file_id']) ? $server_info['file_id'] : $server_info['id']; @@ -1219,25 +1206,28 @@ public function pull( $manual = false ) { ++$total_pulled_ori; }; - $force_wp_remote_get = defined('LITESPEED_FORCE_WP_REMOTE_GET') && LITESPEED_FORCE_WP_REMOTE_GET; - if (!$force_wp_remote_get && class_exists('\WpOrg\Requests\Requests') && class_exists('\WpOrg\Requests\Autoload') && version_compare(PHP_VERSION, '5.6.0', '>=')) { + $force_wp_remote_get = defined('LITESPEED_FORCE_WP_REMOTE_GET') && constant('LITESPEED_FORCE_WP_REMOTE_GET'); + if (!$force_wp_remote_get && class_exists('\WpOrg\Requests\Requests') && class_exists('\WpOrg\Requests\Autoload')) { // Make sure Requests can load internal classes. Autoload::register(); // Run pull requests in parallel - Requests::request_multiple($requests, array( + Requests::request_multiple($requests, [ 'timeout' => 60, 'connect_timeout' => 60, 'complete' => $complete_action, - )); + 'verify' => false, + 'verifyname' => false, + ]); } else { foreach ($requests as $cnt => $req) { - $wp_response = wp_safe_remote_get($req['url'], array( 'timeout' => 60 )); - $request_response = array( + $wp_response = wp_safe_remote_get($req['url'], [ 'timeout' => 60 ]); + $request_response = [ 'success' => false, 'status_code' => 0, 'body' => null, - ); + 'sslverify' => false + ]; if (is_wp_error($wp_response)) { $error_message = $wp_response->get_error_message(); self::debug('❌ failed to pull image: ' . $error_message); @@ -1549,8 +1539,8 @@ private function _rescan() { } // Prepare post_ids to inquery gathered images - $pid_set = array(); - $scanned_list = array(); + $pid_set = []; + $scanned_list = []; foreach ($list as $v) { $meta_value = $this->_parse_wp_meta_value($v); if (!$meta_value) { @@ -1580,7 +1570,9 @@ private function _rescan() { $this->tmp_path = pathinfo($meta_value['file'], PATHINFO_DIRNAME) . '/'; $this->_append_img_queue($meta_value, true); if (!empty($meta_value['sizes'])) { - array_map(array( $this, '_append_img_queue' ), $meta_value['sizes']); + foreach( $meta_value['sizes'] as $img_size_name => $img_size ){ + $this->_append_img_queue($img_size, false, $img_size_name ); + } } } @@ -2125,7 +2117,7 @@ public function check_img() { self::debug('Check image [ID] ' . $pid); - $data = array(); + $data = []; $data['img_count'] = $this->img_count(); $data['optm_summary'] = self::get_summary(); @@ -2136,7 +2128,7 @@ public function check_img() { // Get img_optm data $q = "SELECT * FROM `$this->_table_img_optm` WHERE post_id = %d"; $list = $wpdb->get_results($wpdb->prepare($q, $pid)); - $img_data = array(); + $img_data = []; if ($list) { foreach ($list as $v) { $img_data[] = array( diff --git a/src/import.cls.php b/src/import.cls.php index 4c1138c08..57b2cecff 100644 --- a/src/import.cls.php +++ b/src/import.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The import/export class. @@ -32,7 +33,8 @@ public function __construct() { /** * Export settings to file * - * @since 1.8.2 + * @since 1.8.2 + * @since 7.3 added download content type * @access public */ public function export( $only_data_return = false ) { @@ -58,6 +60,7 @@ public function export( $only_data_return = false ) { Debug2::debug('Import: Saved to ' . $filename); + @header('Content-Type: application/octet-stream'); @header('Content-Disposition: attachment; filename=' . $filename); echo $data; diff --git a/src/import.preset.cls.php b/src/import.preset.cls.php index c54f396e5..b83d58a6b 100644 --- a/src/import.preset.cls.php +++ b/src/import.preset.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The preset class. * diff --git a/src/lang.cls.php b/src/lang.cls.php index 251b35076..cc7107109 100644 --- a/src/lang.cls.php +++ b/src/lang.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The language class. * * @since 3.0 * @package LiteSpeed_Cache - * @subpackage LiteSpeed_Cache/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -141,7 +140,7 @@ public static function title( $id ) { self::O_OPTM_UCSS => __('Generate UCSS', 'litespeed-cache'), self::O_OPTM_UCSS_INLINE => __('UCSS Inline', 'litespeed-cache'), self::O_OPTM_UCSS_SELECTOR_WHITELIST => __('UCSS Selector Allowlist', 'litespeed-cache'), - self::O_OPTM_UCSS_FILE_EXC_INLINE => __('UCSS File Excludes and Inline', 'litespeed-cache'), + self::O_OPTM_UCSS_FILE_EXC_INLINE => __('UCSS Inline Excluded Files', 'litespeed-cache'), self::O_OPTM_UCSS_EXC => __('UCSS URI Excludes', 'litespeed-cache'), self::O_OPTM_JS_MIN => __('JS Minify', 'litespeed-cache'), self::O_OPTM_JS_COMB => __('JS Combine', 'litespeed-cache'), @@ -202,12 +201,14 @@ public static function title( $id ) { self::O_MEDIA_ADD_MISSING_SIZES => __('Add Missing Sizes', 'litespeed-cache'), self::O_MEDIA_VPI => __('Viewport Images', 'litespeed-cache'), self::O_MEDIA_VPI_CRON => __('Viewport Images Cron', 'litespeed-cache'), + self::O_MEDIA_AUTO_RESCALE_ORI => __('Auto Rescale Original Images', 'litespeed-cache'), self::O_IMG_OPTM_AUTO => __('Auto Request Cron', 'litespeed-cache'), self::O_IMG_OPTM_ORI => __('Optimize Original Images', 'litespeed-cache'), self::O_IMG_OPTM_RM_BKUP => __('Remove Original Backups', 'litespeed-cache'), self::O_IMG_OPTM_WEBP => __('Next-Gen Image Format', 'litespeed-cache'), self::O_IMG_OPTM_LOSSLESS => __('Optimize Losslessly', 'litespeed-cache'), + self::O_IMG_OPTM_SIZES_SKIPPED => __('Optimize Image Sizes', 'litespeed-cache'), self::O_IMG_OPTM_EXIF => __('Preserve EXIF/XMP data', 'litespeed-cache'), self::O_IMG_OPTM_WEBP_ATTR => __('WebP/AVIF Attribute To Replace', 'litespeed-cache'), self::O_IMG_OPTM_WEBP_REPLACE_SRCSET => __('WebP/AVIF For Extra srcset', 'litespeed-cache'), @@ -264,6 +265,8 @@ public static function title( $id ) { self::O_DB_OPTM_REVISIONS_MAX => __('Revisions Max Number', 'litespeed-cache'), self::O_DB_OPTM_REVISIONS_AGE => __('Revisions Max Age', 'litespeed-cache'), + + self::O_OPTIMAX => __('OptimaX', 'litespeed-cache'), ); if (array_key_exists($id, $_lang_list)) { diff --git a/src/localization.cls.php b/src/localization.cls.php index 5b7f5cfc8..545daa512 100644 --- a/src/localization.cls.php +++ b/src/localization.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The localization class. diff --git a/src/media.cls.php b/src/media.cls.php index 2f8ca59ef..8b18d0a8f 100644 --- a/src/media.cls.php +++ b/src/media.cls.php @@ -1,137 +1,218 @@ <?php - /** * The class to operate media data. * - * @since 1.4 - * @since 1.5 Moved into /inc - * @package Core - * @subpackage Core/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> + * @package LiteSpeed + * @since 1.4 */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Media + * + * Handles media-related optimizations like lazy loading, next-gen image replacement, and admin UI. + */ class Media extends Root { const LOG_TAG = '📺'; const LIB_FILE_IMG_LAZYLOAD = 'assets/js/lazyload.min.js'; + /** + * Current page buffer content. + * + * @var string + */ private $content; + + /** + * WordPress uploads directory info. + * + * @var array + */ private $_wp_upload_dir; - private $_vpi_preload_list = array(); - private $_format = ''; - private $_sys_format = ''; /** - * Init + * List of VPI (viewport images) to preload in <head>. * - * @since 1.4 + * @var array + */ + private $_vpi_preload_list = []; + + /** + * The user-level next-gen format supported (''|webp|avif). + * + * @var string + */ + private $_format = ''; + + /** + * The system-level chosen next-gen format (webp|avif). + * + * @var string + */ + private $_sys_format = ''; + + /** + * Init. + * + * @since 1.4 */ public function __construct() { - Debug2::debug2('[Media] init'); + self::debug2( 'init' ); $this->_wp_upload_dir = wp_upload_dir(); - if ($this->conf(Base::O_IMG_OPTM_WEBP)) { + if ( $this->conf( Base::O_IMG_OPTM_WEBP ) ) { $this->_sys_format = 'webp'; $this->_format = 'webp'; - if ($this->conf(Base::O_IMG_OPTM_WEBP) == 2) { + if ( 2 === $this->conf( Base::O_IMG_OPTM_WEBP ) ) { $this->_sys_format = 'avif'; $this->_format = 'avif'; } - if (!$this->_browser_support_next_gen()) { + if ( ! $this->_browser_support_next_gen() ) { $this->_format = ''; } - $this->_format = apply_filters('litespeed_next_gen_format', $this->_format); + $this->_format = apply_filters( 'litespeed_next_gen_format', $this->_format ); } } /** - * Hooks after user init + * Hooks after user init. * - * @since 7.2 + * @since 7.2 + * @since 7.4 Add media replace original with scaled. + * @return void */ public function after_user_init() { - // Hook to attachment delete action (PR#844, Issue#841) for AJAX del compatibility - add_action('delete_attachment', array( $this, 'delete_attachment' ), 11, 2); + // Hook to attachment delete action (PR#844, Issue#841) for AJAX del compatibility. + add_action( 'delete_attachment', array( $this, 'delete_attachment' ), 11, 2 ); + + // For big images, allow to replace original with scaled image. + if ( $this->conf( Base::O_MEDIA_AUTO_RESCALE_ORI ) ) { + // Added priority 9 to happen before other functions added. + add_filter( 'wp_update_attachment_metadata', array( $this, 'rescale_ori' ), 9, 2 ); + } } /** - * Init optm features + * Init optm features. * * @since 3.0 * @access public + * @return void */ public function init() { - if (is_admin()) { + if ( is_admin() ) { return; } - // Due to ajax call doesn't send correct accept header, have to limit webp to HTML only - if ($this->webp_support()) { - // Hook to srcset - if (function_exists('wp_calculate_image_srcset')) { - add_filter('wp_calculate_image_srcset', array( $this, 'webp_srcset' ), 988); + // Due to ajax call doesn't send correct accept header, have to limit webp to HTML only. + if ( $this->webp_support() ) { + // Hook to srcset. + if ( function_exists( 'wp_calculate_image_srcset' ) ) { + add_filter( 'wp_calculate_image_srcset', array( $this, 'webp_srcset' ), 988 ); } // Hook to mime icon // add_filter( 'wp_get_attachment_image_src', array( $this, 'webp_attach_img_src' ), 988 );// todo: need to check why not // add_filter( 'wp_get_attachment_url', array( $this, 'webp_url' ), 988 ); // disabled to avoid wp-admin display } - if ($this->conf(Base::O_MEDIA_LAZY) && !$this->cls('Metabox')->setting('litespeed_no_image_lazy')) { - self::debug('Suppress default WP lazyload'); - add_filter('wp_lazy_loading_enabled', '__return_false'); + if ( $this->conf( Base::O_MEDIA_LAZY ) && ! $this->cls( 'Metabox' )->setting( 'litespeed_no_image_lazy' ) ) { + self::debug( 'Suppress default WP lazyload' ); + add_filter( 'wp_lazy_loading_enabled', '__return_false' ); } /** - * Replace gravatar + * Replace gravatar. * - * @since 3.0 + * @since 3.0 */ - $this->cls('Avatar'); + $this->cls( 'Avatar' ); + + add_filter( 'litespeed_buffer_finalize', array( $this, 'finalize' ), 4 ); + add_filter( 'litespeed_optm_html_head', array( $this, 'finalize_head' ) ); + } + + /** + * Handle attachment create (rescale original). + * + * @param array $metadata Current meta array. + * @param int $attachment_id Attachment ID. + * @return array Modified metadata. + * @since 7.4 + */ + public function rescale_ori( $metadata, $attachment_id ) { + // Test if create and image was resized. + if ( $metadata && isset( $metadata['original_image'], $metadata['file'] ) && false !== strpos( $metadata['file'], '-scaled' ) ) { + // Get rescaled file name. + $path_exploded = explode( '/', strrev( $metadata['file'] ), 2 ); + $rescaled_file_name = strrev( $path_exploded[0] ); + + // Create paths for images: resized and original. + $base_path = $this->_wp_upload_dir['basedir'] . $this->_wp_upload_dir['subdir'] . '/'; + $rescaled_path = $base_path . $rescaled_file_name; + $new_path = $base_path . $metadata['original_image']; + + // Change array file key. + $metadata['file'] = $this->_wp_upload_dir['subdir'] . '/' . $metadata['original_image']; + if ( 0 === strpos( $metadata['file'], '/' ) ) { + $metadata['file'] = substr( $metadata['file'], 1 ); + } + + // Delete array "original_image" key. + unset( $metadata['original_image'] ); + + if ( file_exists( $rescaled_path ) && file_exists( $new_path ) ) { + // Move rescaled to original using WP_Filesystem. + global $wp_filesystem; + if ( ! $wp_filesystem ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + \WP_Filesystem(); + } + if ( $wp_filesystem ) { + $wp_filesystem->move( $rescaled_path, $new_path, true ); + } - add_filter('litespeed_buffer_finalize', array( $this, 'finalize' ), 4); + // Update meta "_wp_attached_file". + update_post_meta( $attachment_id, '_wp_attached_file', $metadata['file'] ); + } + } - add_filter('litespeed_optm_html_head', array( $this, 'finalize_head' )); + return $metadata; } /** - * Add featured image to head + * Add featured image and VPI preloads to head. + * + * @param string $content Current head HTML. + * @return string Modified head HTML. */ public function finalize_head( $content ) { - global $wp_query; - // <link rel="preload" as="image" href="xx"> - if ($this->_vpi_preload_list) { - foreach ($this->_vpi_preload_list as $v) { - $content .= '<link rel="preload" as="image" href="' . Str::trim_quotes($v) . '">'; + if ( $this->_vpi_preload_list ) { + foreach ( $this->_vpi_preload_list as $v ) { + $content .= '<link rel="preload" as="image" href="' . esc_url( Str::trim_quotes( $v ) ) . '">'; } } - // $featured_image_url = get_the_post_thumbnail_url(); - // if ($featured_image_url) { - // self::debug('Append featured image to head: ' . $featured_image_url); - // if ($this->webp_support()) { - // $featured_image_url = $this->replace_webp($featured_image_url) ?: $featured_image_url; - // } - // } - // } - return $content; } /** - * Adjust WP default JPG quality + * Adjust WP default JPG quality. * * @since 3.0 * @access public + * + * @param int $quality Current quality. + * @return int Adjusted quality. */ public function adjust_jpg_quality( $quality ) { - $v = $this->conf(Base::O_IMG_OPTM_JPG_QUALITY); + $v = $this->conf( Base::O_IMG_OPTM_JPG_QUALITY ); - if ($v) { + if ( $v ) { return $v; } @@ -139,62 +220,66 @@ public function adjust_jpg_quality( $quality ) { } /** - * Register admin menu + * Register admin menu. * * @since 1.6.3 * @access public + * @return void */ public function after_admin_init() { /** - * JPG quality control + * JPG quality control. * - * @since 3.0 + * @since 3.0 */ - add_filter('jpeg_quality', array( $this, 'adjust_jpg_quality' )); + add_filter( 'jpeg_quality', array( $this, 'adjust_jpg_quality' ) ); - add_filter('manage_media_columns', array( $this, 'media_row_title' )); - add_filter('manage_media_custom_column', array( $this, 'media_row_actions' ), 10, 2); + add_filter( 'manage_media_columns', array( $this, 'media_row_title' ) ); + add_filter( 'manage_media_custom_column', array( $this, 'media_row_actions' ), 10, 2 ); - add_action('litespeed_media_row', array( $this, 'media_row_con' )); + add_action( 'litespeed_media_row', array( $this, 'media_row_con' ) ); } /** - * Media delete action hook + * Media delete action hook. * - * @since 2.4.3 + * @since 2.4.3 * @access public + * + * @param int $post_id Post ID. + * @return void */ public static function delete_attachment( $post_id ) { - // if (!Data::cls()->tb_exist('img_optm')) { - // return; - // } - - self::debug('delete_attachment [pid] ' . $post_id); - Img_Optm::cls()->reset_row($post_id); + self::debug( 'delete_attachment [pid] ' . $post_id ); + Img_Optm::cls()->reset_row( $post_id ); } /** - * Return media file info if exists + * Return media file info if exists. * - * This is for remote attachment plugins + * This is for remote attachment plugins. * - * @since 2.9.8 + * @since 2.9.8 * @access public + * + * @param string $short_file_path Relative file path under uploads. + * @param int $post_id Post ID. + * @return array|false Array( url, md5, size ) or false. */ public function info( $short_file_path, $post_id ) { - $short_file_path = wp_normalize_path($short_file_path); + $short_file_path = wp_normalize_path( $short_file_path ); $basedir = $this->_wp_upload_dir['basedir'] . '/'; - if (strpos($short_file_path, $basedir) === 0) { - $short_file_path = substr($short_file_path, strlen($basedir)); + if ( 0 === strpos( $short_file_path, $basedir ) ) { + $short_file_path = substr( $short_file_path, strlen( $basedir ) ); } $real_file = $basedir . $short_file_path; - if (file_exists($real_file)) { + if ( file_exists( $real_file ) ) { return array( - 'url' => $this->_wp_upload_dir['baseurl'] . '/' . $short_file_path, - 'md5' => md5_file($real_file), - 'size' => filesize($real_file), + 'url' => $this->_wp_upload_dir['baseurl'] . '/' . $short_file_path, + 'md5' => md5_file( $real_file ), + 'size' => filesize( $real_file ), ); } @@ -202,10 +287,10 @@ public function info( $short_file_path, $post_id ) { * WP Stateless compatibility #143 https://github.com/litespeedtech/lscache_wp/issues/143 * * @since 2.9.8 - * @return array( 'url', 'md5', 'size' ) + * Should return array( 'url', 'md5', 'size' ). */ - $info = apply_filters('litespeed_media_info', array(), $short_file_path, $post_id); - if (!empty($info['url']) && !empty($info['md5']) && !empty($info['size'])) { + $info = apply_filters( 'litespeed_media_info', [], $short_file_path, $post_id ); + if ( ! empty( $info['url'] ) && ! empty( $info['md5'] ) && ! empty( $info['size'] ) ) { return $info; } @@ -213,221 +298,274 @@ public function info( $short_file_path, $post_id ) { } /** - * Delete media file + * Delete media file. * - * @since 2.9.8 + * @since 2.9.8 * @access public + * + * @param string $short_file_path Relative file path under uploads. + * @param int $post_id Post ID. + * @return void */ public function del( $short_file_path, $post_id ) { $real_file = $this->_wp_upload_dir['basedir'] . '/' . $short_file_path; - if (file_exists($real_file)) { - unlink($real_file); - self::debug('deleted ' . $real_file); + if ( file_exists( $real_file ) ) { + wp_delete_file( $real_file ); + self::debug( 'deleted ' . $real_file ); } - do_action('litespeed_media_del', $short_file_path, $post_id); + do_action( 'litespeed_media_del', $short_file_path, $post_id ); } /** - * Rename media file + * Rename media file. * - * @since 2.9.8 + * @since 2.9.8 * @access public + * + * @param string $short_file_path Old relative path. + * @param string $short_file_path_new New relative path. + * @param int $post_id Post ID. + * @return void */ public function rename( $short_file_path, $short_file_path_new, $post_id ) { - // self::debug('renaming ' . $short_file_path . ' -> ' . $short_file_path_new); $real_file = $this->_wp_upload_dir['basedir'] . '/' . $short_file_path; $real_file_new = $this->_wp_upload_dir['basedir'] . '/' . $short_file_path_new; - if (file_exists($real_file)) { - rename($real_file, $real_file_new); - self::debug('renamed ' . $real_file . ' to ' . $real_file_new); + if ( file_exists( $real_file ) ) { + global $wp_filesystem; + if ( ! $wp_filesystem ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + \WP_Filesystem(); + } + if ( $wp_filesystem ) { + $wp_filesystem->move( $real_file, $real_file_new, true ); + } + self::debug( 'renamed ' . $real_file . ' to ' . $real_file_new ); } - do_action('litespeed_media_rename', $short_file_path, $short_file_path_new, $post_id); + do_action( 'litespeed_media_rename', $short_file_path, $short_file_path_new, $post_id ); } /** - * Media Admin Menu -> Image Optimization Column Title + * Media Admin Menu -> Image Optimization Column Title. * - * @since 1.6.3 + * @since 1.6.3 * @access public + * + * @param array $posts_columns Existing columns. + * @return array Modified columns. */ public function media_row_title( $posts_columns ) { - $posts_columns['imgoptm'] = __('LiteSpeed Optimization', 'litespeed-cache'); - + $posts_columns['imgoptm'] = esc_html__( 'LiteSpeed Optimization', 'litespeed-cache' ); return $posts_columns; } /** - * Media Admin Menu -> Image Optimization Column + * Media Admin Menu -> Image Optimization Column. * - * @since 1.6.2 + * @since 1.6.2 * @access public + * + * @param string $column_name Current column name. + * @param int $post_id Post ID. + * @return void */ public function media_row_actions( $column_name, $post_id ) { - if ($column_name !== 'imgoptm') { + if ( 'imgoptm' !== $column_name ) { return; } - do_action('litespeed_media_row', $post_id); + do_action( 'litespeed_media_row', $post_id ); } /** - * Display image optm info + * Display image optimization info in the media list row. * - * @since 3.0 + * @since 3.0 + * + * @param int $post_id Attachment post ID. + * @return void */ public function media_row_con( $post_id ) { - $att_info = wp_get_attachment_metadata($post_id); - if (empty($att_info['file'])) { + $att_info = wp_get_attachment_metadata( $post_id ); + if ( empty( $att_info['file'] ) ) { return; } $short_path = $att_info['file']; - $size_meta = get_post_meta($post_id, Img_Optm::DB_SIZE, true); + $size_meta = get_post_meta( $post_id, Img_Optm::DB_SIZE, true ); echo '<p>'; - // Original image info - if ($size_meta && !empty($size_meta['ori_saved'])) { - $percent = ceil(($size_meta['ori_saved'] * 100) / $size_meta['ori_total']); + // Original image info. + if ( $size_meta && ! empty( $size_meta['ori_saved'] ) ) { + $percent = (int) ceil( ( (int) $size_meta['ori_saved'] * 100 ) / max( 1, (int) $size_meta['ori_total'] ) ); - $extension = pathinfo($short_path, PATHINFO_EXTENSION); - $bk_file = substr($short_path, 0, -strlen($extension)) . 'bk.' . $extension; - $bk_optm_file = substr($short_path, 0, -strlen($extension)) . 'bk.optm.' . $extension; + $extension = pathinfo( $short_path, PATHINFO_EXTENSION ); + $bk_file = substr( $short_path, 0, -strlen( $extension ) ) . 'bk.' . $extension; + $bk_optm_file = substr( $short_path, 0, -strlen( $extension ) ) . 'bk.optm.' . $extension; - $link = Utility::build_url(Router::ACTION_IMG_OPTM, 'orig' . $post_id); + $link = Utility::build_url( Router::ACTION_IMG_OPTM, 'orig' . $post_id ); $desc = false; $cls = ''; - if ($this->info($bk_file, $post_id)) { - $curr_status = __('(optm)', 'litespeed-cache'); - $desc = __('Currently using optimized version of file.', 'litespeed-cache') . ' ' . __('Click to switch to original (unoptimized) version.', 'litespeed-cache'); - } elseif ($this->info($bk_optm_file, $post_id)) { + if ( $this->info( $bk_file, $post_id ) ) { + $curr_status = esc_html__( '(optm)', 'litespeed-cache' ); + $desc = esc_attr__( 'Currently using optimized version of file.', 'litespeed-cache' ) . ' ' . esc_attr__( 'Click to switch to original (unoptimized) version.', 'litespeed-cache' ); + } elseif ( $this->info( $bk_optm_file, $post_id ) ) { $cls .= ' litespeed-warning'; - $curr_status = __('(non-optm)', 'litespeed-cache'); - $desc = __('Currently using original (unoptimized) version of file.', 'litespeed-cache') . ' ' . __('Click to switch to optimized version.', 'litespeed-cache'); + $curr_status = esc_html__( '(non-optm)', 'litespeed-cache' ); + $desc = esc_attr__( 'Currently using original (unoptimized) version of file.', 'litespeed-cache' ) . ' ' . esc_attr__( 'Click to switch to optimized version.', 'litespeed-cache' ); } - echo GUI::pie_tiny( - $percent, - 24, - sprintf(__('Original file reduced by %1$s (%2$s)', 'litespeed-cache'), $percent . '%', Utility::real_size($size_meta['ori_saved'])), - 'left' + echo wp_kses_post( + GUI::pie_tiny( + $percent, + 24, + sprintf( + esc_html__( 'Original file reduced by %1$s (%2$s)', 'litespeed-cache' ), + $percent . '%', + Utility::real_size( $size_meta['ori_saved'] ) + ), + 'left' + ) ); - printf(__('Orig saved %s', 'litespeed-cache'), $percent . '%'); + printf( + esc_html__( 'Orig saved %s', 'litespeed-cache' ), + (int) $percent . '%' + ); - if ($desc) { - printf(' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', $link, $cls, $desc, $curr_status); + if ( $desc ) { + printf( + ' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', + esc_url( $link ), + esc_attr( $cls ), + wp_kses_post( $desc ), + esc_html( $curr_status ) + ); } else { printf( ' <span class="litespeed-desc" data-balloon-pos="left" data-balloon-break aria-label="%1$s">%2$s</span>', - __('Using optimized version of file. ', 'litespeed-cache') . ' ' . __('No backup of original file exists.', 'litespeed-cache'), - __('(optm)', 'litespeed-cache') + esc_attr__( 'Using optimized version of file. ', 'litespeed-cache' ) . ' ' . esc_attr__( 'No backup of original file exists.', 'litespeed-cache' ), + esc_html__( '(optm)', 'litespeed-cache' ) ); } - } elseif ($size_meta && $size_meta['ori_saved'] === 0) { - echo GUI::pie_tiny(0, 24, __('Congratulation! Your file was already optimized', 'litespeed-cache'), 'left'); - printf(__('Orig %s', 'litespeed-cache'), '<span class="litespeed-desc">' . __('(no savings)', 'litespeed-cache') . '</span>'); + } elseif ( $size_meta && 0 === (int) $size_meta['ori_saved'] ) { + echo wp_kses_post( GUI::pie_tiny( 0, 24, esc_html__( 'Congratulation! Your file was already optimized', 'litespeed-cache' ), 'left' ) ); + printf( + esc_html__( 'Orig %s', 'litespeed-cache' ), + '<span class="litespeed-desc">' . esc_html__( '(no savings)', 'litespeed-cache' ) . '</span>' + ); } else { - echo __('Orig', 'litespeed-cache') . '<span class="litespeed-left10">—</span>'; + echo esc_html__( 'Orig', 'litespeed-cache' ) . '<span class="litespeed-left10">—</span>'; } echo '</p>'; echo '<p>'; - // WebP/AVIF info - if ($size_meta && $this->webp_support(true) && !empty($size_meta[$this->_sys_format . '_saved'])) { + // WebP/AVIF info. + if ( $size_meta && $this->webp_support( true ) && ! empty( $size_meta[ $this->_sys_format . '_saved' ] ) ) { $is_avif = 'avif' === $this->_sys_format; - $size_meta_saved = $size_meta[$this->_sys_format . '_saved']; - $size_meta_total = $size_meta[$this->_sys_format . '_total']; + $size_meta_saved = $size_meta[ $this->_sys_format . '_saved' ]; + $size_meta_total = $size_meta[ $this->_sys_format . '_total' ]; - $percent = ceil(($size_meta_saved * 100) / $size_meta_total); + $percent = ceil( ( $size_meta_saved * 100 ) / max( 1, $size_meta_total ) ); - $link = Utility::build_url(Router::ACTION_IMG_OPTM, $this->_sys_format . $post_id); + $link = Utility::build_url( Router::ACTION_IMG_OPTM, $this->_sys_format . $post_id ); $desc = false; $cls = ''; - if ($this->info($short_path . '.' . $this->_sys_format, $post_id)) { - $curr_status = __('(optm)', 'litespeed-cache'); + if ( $this->info( $short_path . '.' . $this->_sys_format, $post_id ) ) { + $curr_status = esc_html__( '(optm)', 'litespeed-cache' ); $desc = $is_avif - ? __('Currently using optimized version of AVIF file.', 'litespeed-cache') - : __('Currently using optimized version of WebP file.', 'litespeed-cache'); - $desc .= ' ' . __('Click to switch to original (unoptimized) version.', 'litespeed-cache'); - } elseif ($this->info($short_path . '.optm.' . $this->_sys_format, $post_id)) { + ? esc_attr__( 'Currently using optimized version of AVIF file.', 'litespeed-cache' ) + : esc_attr__( 'Currently using optimized version of WebP file.', 'litespeed-cache' ); + $desc .= ' ' . esc_attr__( 'Click to switch to original (unoptimized) version.', 'litespeed-cache' ); + } elseif ( $this->info( $short_path . '.optm.' . $this->_sys_format, $post_id ) ) { $cls .= ' litespeed-warning'; - $curr_status = __('(non-optm)', 'litespeed-cache'); + $curr_status = esc_html__( '(non-optm)', 'litespeed-cache' ); $desc = $is_avif - ? __('Currently using original (unoptimized) version of AVIF file.', 'litespeed-cache') - : __('Currently using original (unoptimized) version of WebP file.', 'litespeed-cache'); - $desc .= ' ' . __('Click to switch to optimized version.', 'litespeed-cache'); + ? esc_attr__( 'Currently using original (unoptimized) version of AVIF file.', 'litespeed-cache' ) + : esc_attr__( 'Currently using original (unoptimized) version of WebP file.', 'litespeed-cache' ); + $desc .= ' ' . esc_attr__( 'Click to switch to optimized version.', 'litespeed-cache' ); } - echo GUI::pie_tiny( - $percent, - 24, - sprintf( - $is_avif ? __('AVIF file reduced by %1$s (%2$s)', 'litespeed-cache') : __('WebP file reduced by %1$s (%2$s)', 'litespeed-cache'), - $percent . '%', - Utility::real_size($size_meta_saved) - ), - 'left' + echo wp_kses_post( + GUI::pie_tiny( + $percent, + 24, + sprintf( + $is_avif ? esc_html__( 'AVIF file reduced by %1$s (%2$s)', 'litespeed-cache' ) : esc_html__( 'WebP file reduced by %1$s (%2$s)', 'litespeed-cache' ), + $percent . '%', + Utility::real_size( $size_meta_saved ) + ), + 'left' + ) + ); + printf( + $is_avif ? esc_html__( 'AVIF saved %s', 'litespeed-cache' ) : esc_html__( 'WebP saved %s', 'litespeed-cache' ), + '<span>' . esc_html( $percent ) . '%</span>' ); - printf($is_avif ? __('AVIF saved %s', 'litespeed-cache') : __('WebP saved %s', 'litespeed-cache'), $percent . '%'); - if ($desc) { - printf(' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', $link, $cls, $desc, $curr_status); + if ( $desc ) { + printf( + ' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', + esc_url( $link ), + esc_attr( $cls ), + wp_kses_post( $desc ), + esc_html( $curr_status ) + ); } else { printf( ' <span class="litespeed-desc" data-balloon-pos="left" data-balloon-break aria-label="%1$s %2$s">%3$s</span>', - __('Using optimized version of file. ', 'litespeed-cache'), - $is_avif ? __('No backup of unoptimized AVIF file exists.', 'litespeed-cache') : __('No backup of unoptimized WebP file exists.', 'litespeed-cache'), - __('(optm)', 'litespeed-cache') + esc_attr__( 'Using optimized version of file. ', 'litespeed-cache' ), + $is_avif ? esc_attr__( 'No backup of unoptimized AVIF file exists.', 'litespeed-cache' ) : esc_attr__( 'No backup of unoptimized WebP file exists.', 'litespeed-cache' ), + esc_html__( '(optm)', 'litespeed-cache' ) ); } } else { - echo $this->next_gen_image_title() . '<span class="litespeed-left10">—</span>'; + echo esc_html( $this->next_gen_image_title() ) . '<span class="litespeed-left10">—</span>'; } echo '</p>'; - // Delete row btn - if ($size_meta) { + // Delete row btn. + if ( $size_meta ) { printf( '<div class="row-actions"><span class="delete"><a href="%1$s" class="">%2$s</a></span></div>', - Utility::build_url(Router::ACTION_IMG_OPTM, Img_Optm::TYPE_RESET_ROW, false, null, array( 'id' => $post_id )), - __('Restore from backup', 'litespeed-cache') + esc_url( Utility::build_url( Router::ACTION_IMG_OPTM, Img_Optm::TYPE_RESET_ROW, false, null, array( 'id' => $post_id ) ) ), + esc_html__( 'Restore from backup', 'litespeed-cache' ) ); echo '</div>'; } } /** - * Get wp size info + * Get wp size info. * - * NOTE: this is not used because it has to be after admin_init + * NOTE: this is not used because it has to be after admin_init. * * @since 1.6.2 * @return array $sizes Data for all currently-registered image sizes. */ public function get_image_sizes() { global $_wp_additional_image_sizes; - $sizes = array(); - - foreach (get_intermediate_image_sizes() as $_size) { - if (in_array($_size, array( 'thumbnail', 'medium', 'medium_large', 'large' ))) { - $sizes[$_size]['width'] = get_option($_size . '_size_w'); - $sizes[$_size]['height'] = get_option($_size . '_size_h'); - $sizes[$_size]['crop'] = (bool) get_option($_size . '_crop'); - } elseif (isset($_wp_additional_image_sizes[$_size])) { - $sizes[$_size] = array( - 'width' => $_wp_additional_image_sizes[$_size]['width'], - 'height' => $_wp_additional_image_sizes[$_size]['height'], - 'crop' => $_wp_additional_image_sizes[$_size]['crop'], + $sizes = []; + + foreach ( get_intermediate_image_sizes() as $_size ) { + if ( in_array( $_size, array( 'thumbnail', 'medium', 'medium_large', 'large' ), true ) ) { + $sizes[ $_size ]['width'] = get_option( $_size . '_size_w' ); + $sizes[ $_size ]['height'] = get_option( $_size . '_size_h' ); + $sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' ); + } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) { + $sizes[ $_size ] = array( + 'width' => $_wp_additional_image_sizes[ $_size ]['width'], + 'height' => $_wp_additional_image_sizes[ $_size ]['height'], + 'crop' => $_wp_additional_image_sizes[ $_size ]['crop'], ); } } @@ -436,40 +574,51 @@ public function get_image_sizes() { } /** - * Exclude role from optimization filter + * Exclude role from optimization filter. * * @since 1.6.2 * @access public + * + * @param bool $sys_level Return system-level format if true. + * @return string Next-gen format name or empty string. */ public function webp_support( $sys_level = false ) { - if ($sys_level) { + if ( $sys_level ) { return $this->_sys_format; } - return $this->_format; // User level next gen support + return $this->_format; // User level next gen support. } + + /** + * Detect if browser supports next-gen format. + * + * @return bool + */ private function _browser_support_next_gen() { - if (!empty($_SERVER['HTTP_ACCEPT'])) { - if (strpos($_SERVER['HTTP_ACCEPT'], 'image/' . $this->_sys_format) !== false) { + $accept = isset( $_SERVER['HTTP_ACCEPT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT'] ) ) : ''; + if ( $accept ) { + if ( false !== strpos( $accept, 'image/' . $this->_sys_format ) ) { return true; } } - if (!empty($_SERVER['HTTP_USER_AGENT'])) { + $ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; + if ( $ua ) { $user_agents = array( 'chrome-lighthouse', 'googlebot', 'page speed' ); - foreach ($user_agents as $user_agent) { - if (stripos($_SERVER['HTTP_USER_AGENT'], $user_agent) !== false) { + foreach ( $user_agents as $user_agent ) { + if ( false !== stripos( $ua, $user_agent ) ) { return true; } } - if (preg_match('/iPhone OS (\d+)_/i', $_SERVER['HTTP_USER_AGENT'], $matches)) { - if ($matches[1] >= 14) { + if ( preg_match( '/iPhone OS (\d+)_/i', $ua, $matches ) ) { + if ( $matches[1] >= 14 ) { return true; } } - if (preg_match('/Firefox\/(\d+)/i', $_SERVER['HTTP_USER_AGENT'], $matches)) { - if ($matches[1] >= 65) { + if ( preg_match( '/Firefox\/(\d+)/i', $ua, $matches ) ) { + if ( $matches[1] >= 65 ) { return true; } } @@ -479,45 +628,48 @@ private function _browser_support_next_gen() { } /** - * Get next gen image title + * Get next gen image title. * * @since 7.0 + * @return string */ public function next_gen_image_title() { $next_gen_img = 'WebP'; - if ($this->conf(Base::O_IMG_OPTM_WEBP) == 2) { + if ( 2 === $this->conf( Base::O_IMG_OPTM_WEBP ) ) { $next_gen_img = 'AVIF'; } return $next_gen_img; } /** - * Run lazy load process - * NOTE: As this is after cache finalized, can NOT set any cache control anymore + * Run lazy load process. + * NOTE: As this is after cache finalized, can NOT set any cache control anymore. * * Only do for main page. Do NOT do for esi or dynamic content. * * @since 1.4 * @access public - * @return string The buffer + * + * @param string $content Final buffer. + * @return string The buffer. */ public function finalize( $content ) { - if (defined('LITESPEED_NO_LAZY')) { - Debug2::debug2('[Media] bypass: NO_LAZY const'); + if ( defined( 'LITESPEED_NO_LAZY' ) ) { + self::debug2( 'bypass: NO_LAZY const' ); return $content; } - if (!defined('LITESPEED_IS_HTML')) { - Debug2::debug2('[Media] bypass: Not frontend HTML type'); + if ( ! defined( 'LITESPEED_IS_HTML' ) ) { + self::debug2( 'bypass: Not frontend HTML type' ); return $content; } - if (!Control::is_cacheable()) { - self::debug('bypass: Not cacheable'); + if ( ! Control::is_cacheable() ) { + self::debug( 'bypass: Not cacheable' ); return $content; } - self::debug('finalize'); + self::debug( 'finalize' ); $this->content = $content; $this->_finalize(); @@ -525,304 +677,333 @@ public function finalize( $content ) { } /** - * Run lazyload replacement for images in buffer + * Run lazyload replacement for images in buffer. * * @since 1.4 * @access private + * @return void */ private function _finalize() { /** - * Use webp for optimized images + * Use webp for optimized images. * * @since 1.6.2 */ - if ($this->webp_support()) { - $this->content = $this->_replace_buffer_img_webp($this->content); + if ( $this->webp_support() ) { + $this->content = $this->_replace_buffer_img_webp( $this->content ); } /** - * Check if URI is excluded + * Check if URI is excluded. * - * @since 3.0 + * @since 3.0 */ - $excludes = $this->conf(Base::O_MEDIA_LAZY_URI_EXC); - if (!defined('LITESPEED_GUEST_OPTM')) { - $result = Utility::str_hit_array($_SERVER['REQUEST_URI'], $excludes); - if ($result) { - self::debug('bypass lazyload: hit URI Excludes setting: ' . $result); + $excludes = $this->conf( Base::O_MEDIA_LAZY_URI_EXC ); + if ( ! defined( 'LITESPEED_GUEST_OPTM' ) ) { + $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + $result = $request_uri ? Utility::str_hit_array( $request_uri, $excludes ) : false; + if ( $result ) { + self::debug( 'bypass lazyload: hit URI Excludes setting: ' . $result ); return; } } - $cfg_lazy = (defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_MEDIA_LAZY)) && !$this->cls('Metabox')->setting('litespeed_no_image_lazy'); - $cfg_iframe_lazy = defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_MEDIA_IFRAME_LAZY); - $cfg_js_delay = defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_OPTM_JS_DEFER) == 2; - $cfg_trim_noscript = defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_OPTM_NOSCRIPT_RM); - $cfg_vpi = defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_MEDIA_VPI); + $cfg_lazy = ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_MEDIA_LAZY ) ) && ! $this->cls( 'Metabox' )->setting( 'litespeed_no_image_lazy' ); + $cfg_iframe_lazy = defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_MEDIA_IFRAME_LAZY ); + $cfg_js_delay = defined( 'LITESPEED_GUEST_OPTM' ) || 2 === $this->conf( Base::O_OPTM_JS_DEFER ); + $cfg_trim_noscript = defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_OPTM_NOSCRIPT_RM ); + $cfg_vpi = defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_MEDIA_VPI ); - // Preload VPI - if ($cfg_vpi) { + // Preload VPI. + if ( $cfg_vpi ) { $this->_parse_img_for_preload(); } - if ($cfg_lazy) { - if ($cfg_vpi) { - add_filter('litespeed_media_lazy_img_excludes', array( $this->cls('Metabox'), 'lazy_img_excludes' )); + if ( $cfg_lazy ) { + if ( $cfg_vpi ) { + add_filter( 'litespeed_media_lazy_img_excludes', array( $this->cls( 'Metabox' ), 'lazy_img_excludes' ) ); } - list($src_list, $html_list, $placeholder_list) = $this->_parse_img(); - $html_list_ori = $html_list; + list( $src_list, $html_list, $placeholder_list ) = $this->_parse_img(); + $html_list_ori = $html_list; } else { - self::debug('lazyload disabled'); + self::debug( 'lazyload disabled' ); } - // image lazy load - if ($cfg_lazy) { + // image lazy load. + if ( $cfg_lazy ) { $__placeholder = Placeholder::cls(); - foreach ($html_list as $k => $v) { - $size = $placeholder_list[$k]; - $src = $src_list[$k]; + foreach ( $html_list as $k => $v ) { + $size = $placeholder_list[ $k ]; + $src = $src_list[ $k ]; - $html_list[$k] = $__placeholder->replace($v, $src, $size); + $html_list[ $k ] = $__placeholder->replace( $v, $src, $size ); } } - if ($cfg_lazy) { - $this->content = str_replace($html_list_ori, $html_list, $this->content); + if ( $cfg_lazy ) { + $this->content = str_replace( $html_list_ori, $html_list, $this->content ); } - // iframe lazy load - if ($cfg_iframe_lazy) { + // iframe lazy load. + if ( $cfg_iframe_lazy ) { $html_list = $this->_parse_iframe(); $html_list_ori = $html_list; - foreach ($html_list as $k => $v) { + foreach ( $html_list as $k => $v ) { $snippet = $cfg_trim_noscript ? '' : '<noscript>' . $v . '</noscript>'; - if ($cfg_js_delay) { - $v = str_replace(' src=', ' data-litespeed-src=', $v); + if ( $cfg_js_delay ) { + $v = str_replace( ' src=', ' data-litespeed-src=', $v ); } else { - $v = str_replace(' src=', ' data-src=', $v); + $v = str_replace( ' src=', ' data-src=', $v ); } - $v = str_replace('<iframe ', '<iframe data-lazyloaded="1" src="about:blank" ', $v); + $v = str_replace( '<iframe ', '<iframe data-lazyloaded="1" src="about:blank" ', $v ); $snippet = $v . $snippet; - $html_list[$k] = $snippet; + $html_list[ $k ] = $snippet; } - $this->content = str_replace($html_list_ori, $html_list, $this->content); + $this->content = str_replace( $html_list_ori, $html_list, $this->content ); } - // Include lazyload lib js and init lazyload - if ($cfg_lazy || $cfg_iframe_lazy) { - $lazy_lib = '<script data-no-optimize="1">' . File::read(LSCWP_DIR . self::LIB_FILE_IMG_LAZYLOAD) . '</script>'; - if ($cfg_js_delay) { - // Load JS delay lib - if (!defined('LITESPEED_JS_DELAY_LIB_LOADED')) { - define('LITESPEED_JS_DELAY_LIB_LOADED', true); - $lazy_lib .= '<script data-no-optimize="1">' . File::read(LSCWP_DIR . Optimize::LIB_FILE_JS_DELAY) . '</script>'; + // Include lazyload lib js and init lazyload. + if ( $cfg_lazy || $cfg_iframe_lazy ) { + $lazy_lib = '<script data-no-optimize="1">window.lazyLoadOptions=Object.assign({},{threshold:' . apply_filters( 'litespeed_lazyload_threshold', 300 ) . '},window.lazyLoadOptions||{});' . File::read( LSCWP_DIR . self::LIB_FILE_IMG_LAZYLOAD ) . '</script>'; + if ( $cfg_js_delay ) { + // Load JS delay lib. + if ( ! defined( 'LITESPEED_JS_DELAY_LIB_LOADED' ) ) { + define( 'LITESPEED_JS_DELAY_LIB_LOADED', true ); + $lazy_lib .= '<script data-no-optimize="1">' . File::read( LSCWP_DIR . Optimize::LIB_FILE_JS_DELAY ) . '</script>'; } } - $this->content = str_replace('</body>', $lazy_lib . '</body>', $this->content); + $this->content = str_replace( '</body>', $lazy_lib . '</body>', $this->content ); } } /** - * Parse img src for VPI preload only - * Note: Didn't reuse the _parse_img() bcoz it contains parent cls replacement and other logic which is not needed for preload + * Parse img src for VPI preload only. + * Note: Didn't reuse the _parse_img() because it contains replacement logic which is not needed for preload. * * @since 6.2 + * @since 7.6 - Added attributes fetchpriority="high" and decode="sync" for VPI images. + * @return void */ private function _parse_img_for_preload() { - // Load VPI setting + // Load VPI setting. $is_mobile = $this->_separate_mobile(); - $vpi_files = $this->cls('Metabox')->setting($is_mobile ? 'litespeed_vpi_list_mobile' : 'litespeed_vpi_list'); - if ($vpi_files) { - $vpi_files = Utility::sanitize_lines($vpi_files, 'basename'); + $vpi_files = $this->cls( 'Metabox' )->setting( $is_mobile ? 'litespeed_vpi_list_mobile' : 'litespeed_vpi_list' ); + if ( $vpi_files ) { + $vpi_files = Utility::sanitize_lines( $vpi_files, 'basename' ); } - if (!$vpi_files) { + if ( ! $vpi_files ) { return; } - if (!$this->content) { + if ( ! $this->content ) { return; } - $content = preg_replace(array( '#<!--.*-->#sU', '#<noscript([^>]*)>.*</noscript>#isU' ), '', $this->content); - if (!$content) { + $content = preg_replace( array( '#<!--.*-->#sU', '#<noscript([^>]*)>.*</noscript>#isU' ), '', $this->content ); + if ( ! $content ) { return; } + $vpi_fp_search = []; + $vpi_fp_replace = []; preg_match_all('#<img\s+([^>]+)/?>#isU', $content, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $attrs = Utility::parse_attr($match[1]); - if (empty($attrs['src'])) { + if ( empty( $attrs['src'] ) ) { continue; } - if (strpos($attrs['src'], 'base64') !== false || substr($attrs['src'], 0, 5) === 'data:') { - Debug2::debug2('[Media] lazyload bypassed base64 img'); + if ( false !== strpos( $attrs['src'], 'base64' ) || 0 === strpos( $attrs['src'], 'data:' ) ) { + self::debug2( 'lazyload bypassed base64 img' ); continue; } - if (strpos($attrs['src'], '{') !== false) { - Debug2::debug2('[Media] image src has {} ' . $attrs['src']); + if ( false !== strpos( $attrs['src'], '{' ) ) { + self::debug2( 'image src has {} ' . $attrs['src'] ); continue; } - // If the src contains VPI filename, then preload it - if (!Utility::str_hit_array($attrs['src'], $vpi_files)) { + // If the src contains VPI filename, then preload it. + if ( ! Utility::str_hit_array( $attrs['src'], $vpi_files ) ) { continue; } - Debug2::debug2('[Media] VPI preload found and matched: ' . $attrs['src']); + self::debug2( 'VPI preload found and matched: ' . $attrs['src'] ); $this->_vpi_preload_list[] = $attrs['src']; + + // Add attributes fetchpriority="high" and decode="sync" + // after WP 6.3.0 use: wp_img_tag_add_loading_optimization_attrs(). + $new_html = []; + $attrs[ 'fetchpriority' ] = 'high'; + $attrs[ 'decoding' ] = 'sync'; + // create html with new attributes. + foreach ( $attrs as $k => $attr ) { + $new_html[] = $k . '="' . $attr . '"'; + } + + if ( $new_html ) { + $vpi_fp_search[] = $match[1]; + $vpi_fp_replace[] = implode( ' ', $new_html); + } + } + + // if VPI fetchpriority changes, do the replacement + if ( $vpi_fp_search && $vpi_fp_replace ) { + $this->content = str_replace( $vpi_fp_search, $vpi_fp_replace, $this->content ); } + unset( $vpi_fp_search ); + unset( $vpi_fp_replace ); } /** - * Parse img src + * Parse img src. * * @since 1.4 * @access private - * @return array All the src & related raw html list + * @return array{0:array,1:array,2:array} All the src & related raw html list with placeholders. */ private function _parse_img() { /** - * Exclude list + * Exclude list. * * @since 1.5 - * @since 2.7.1 Changed to array + * @since 2.7.1 Changed to array. */ - $excludes = apply_filters('litespeed_media_lazy_img_excludes', $this->conf(Base::O_MEDIA_LAZY_EXC)); + $excludes = apply_filters( 'litespeed_media_lazy_img_excludes', $this->conf( Base::O_MEDIA_LAZY_EXC ) ); - $cls_excludes = apply_filters('litespeed_media_lazy_img_cls_excludes', $this->conf(Base::O_MEDIA_LAZY_CLS_EXC)); + $cls_excludes = apply_filters( 'litespeed_media_lazy_img_cls_excludes', $this->conf( Base::O_MEDIA_LAZY_CLS_EXC ) ); $cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427 - $src_list = array(); - $html_list = array(); - $placeholder_list = array(); + $src_list = []; + $html_list = []; + $placeholder_list = []; $content = preg_replace( array( '#<!--.*-->#sU', '#<noscript([^>]*)>.*</noscript>#isU', - '#<script([^>]*)>.*</script>#isU', // Added to remove warning of file not found when image size detection is turned ON. + '#<script([^>]*)>.*</script>#isU', // Remove script to avoid false matches and warnings, when image size detection is turned ON. ), '', $this->content ); /** - * Exclude parent classes + * Exclude parent classes. * - * @since 3.0 + * @since 3.0 */ - $parent_cls_exc = apply_filters('litespeed_media_lazy_img_parent_cls_excludes', $this->conf(Base::O_MEDIA_LAZY_PARENT_CLS_EXC)); - if ($parent_cls_exc) { - Debug2::debug2('[Media] Lazyload Class excludes', $parent_cls_exc); - foreach ($parent_cls_exc as $v) { + $parent_cls_exc = apply_filters( 'litespeed_media_lazy_img_parent_cls_excludes', $this->conf( Base::O_MEDIA_LAZY_PARENT_CLS_EXC ) ); + if ( $parent_cls_exc ) { + self::debug2( 'Lazyload Class excludes', $parent_cls_exc ); + foreach ( $parent_cls_exc as $v ) { $content = preg_replace('#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote($v, '#') . '[^\'"]*\2[^>]*>.*</\1>#sU', '', $content); } } - preg_match_all('#<img\s+([^>]+)/?>#isU', $content, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $attrs = Utility::parse_attr($match[1]); + preg_match_all( '#<img\s+([^>]+)/?>#isU', $content, $matches, PREG_SET_ORDER ); + foreach ( $matches as $match ) { + $attrs = Utility::parse_attr( $match[1] ); - if (empty($attrs['src'])) { + if ( empty( $attrs['src'] ) ) { continue; } /** - * Add src validation to bypass base64 img src + * Add src validation to bypass base64 img src. * - * @since 1.6 + * @since 1.6 */ - if (strpos($attrs['src'], 'base64') !== false || substr($attrs['src'], 0, 5) === 'data:') { - Debug2::debug2('[Media] lazyload bypassed base64 img'); + if ( false !== strpos( $attrs['src'], 'base64' ) || 0 === strpos( $attrs['src'], 'data:' ) ) { + self::debug2( 'lazyload bypassed base64 img' ); continue; } - Debug2::debug2('[Media] lazyload found: ' . $attrs['src']); + self::debug2( 'lazyload found: ' . $attrs['src'] ); if ( - !empty($attrs['data-no-lazy']) || - !empty($attrs['data-skip-lazy']) || - !empty($attrs['data-lazyloaded']) || - !empty($attrs['data-src']) || - !empty($attrs['data-srcset']) + ! empty( $attrs['data-no-lazy'] ) || + ! empty( $attrs['data-skip-lazy'] ) || + ! empty( $attrs['data-lazyloaded'] ) || + ! empty( $attrs['data-src'] ) || + ! empty( $attrs['data-srcset'] ) ) { - Debug2::debug2('[Media] bypassed'); + self::debug2( 'bypassed' ); continue; } - if (!empty($attrs['class']) && ($hit = Utility::str_hit_array($attrs['class'], $cls_excludes))) { - Debug2::debug2('[Media] lazyload image cls excludes [hit] ' . $hit); + $hit = ! empty( $attrs['class'] ) ? Utility::str_hit_array( $attrs['class'], $cls_excludes ) : false; + if ( $hit ) { + self::debug2( 'lazyload image cls excludes [hit] ' . $hit ); continue; } /** - * Exclude from lazyload by setting + * Exclude from lazyload by setting. * - * @since 1.5 + * @since 1.5 */ - if ($excludes && Utility::str_hit_array($attrs['src'], $excludes)) { - Debug2::debug2('[Media] lazyload image exclude ' . $attrs['src']); + if ( $excludes && Utility::str_hit_array( $attrs['src'], $excludes ) ) { + self::debug2( 'lazyload image exclude ' . $attrs['src'] ); continue; } /** - * Excldues invalid image src from buddypress avatar crop + * Excludes invalid image src from buddypress avatar crop. * - * @see https://wordpress.org/support/topic/lazy-load-breaking-buddypress-upload-avatar-feature - * @since 3.0 + * @see https://wordpress.org/support/topic/lazy-load-breaking-buddypress-upload-avatar-feature + * @since 3.0 */ - if (strpos($attrs['src'], '{') !== false) { - Debug2::debug2('[Media] image src has {} ' . $attrs['src']); + if ( false !== strpos( $attrs['src'], '{' ) ) { + self::debug2( 'image src has {} ' . $attrs['src'] ); continue; } - // to avoid multiple replacement - if (in_array($match[0], $html_list)) { + // to avoid multiple replacement. + if ( in_array( $match[0], $html_list, true ) ) { continue; } - // Add missing dimensions - if (defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_MEDIA_ADD_MISSING_SIZES)) { - if (!apply_filters('litespeed_media_add_missing_sizes', true)) { - Debug2::debug2('[Media] add_missing_sizes bypassed via litespeed_media_add_missing_sizes filter'); - } elseif (empty($attrs['width']) || $attrs['width'] == 'auto' || empty($attrs['height']) || $attrs['height'] == 'auto') { - self::debug('⚠️ Missing sizes for image [src] ' . $attrs['src']); - $dimensions = $this->_detect_dimensions($attrs['src']); - if ($dimensions) { + // Add missing dimensions. + if ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_MEDIA_ADD_MISSING_SIZES ) ) { + if ( ! apply_filters( 'litespeed_media_add_missing_sizes', true ) ) { + self::debug2( 'add_missing_sizes bypassed via litespeed_media_add_missing_sizes filter' ); + } elseif ( empty( $attrs['width'] ) || 'auto' === $attrs['width'] || empty( $attrs['height'] ) || 'auto' === $attrs['height'] ) { + self::debug( '⚠️ Missing sizes for image [src] ' . $attrs['src'] ); + $dimensions = $this->_detect_dimensions( $attrs['src'] ); + if ( $dimensions ) { $ori_width = $dimensions[0]; $ori_height = $dimensions[1]; - // Calculate height based on width - if (!empty($attrs['width']) && $attrs['width'] != 'auto') { - $ori_height = intval(($ori_height * $attrs['width']) / $ori_width); - } elseif (!empty($attrs['height']) && $attrs['height'] != 'auto') { - $ori_width = intval(($ori_width * $attrs['height']) / $ori_height); + // Calculate height based on width. + if ( ! empty( $attrs['width'] ) && 'auto' !== $attrs['width'] ) { + $ori_height = (int) ( ( $ori_height * (int) $attrs['width'] ) / max( 1, $ori_width ) ); + } elseif ( ! empty( $attrs['height'] ) && 'auto' !== $attrs['height'] ) { + $ori_width = (int) ( ( $ori_width * (int) $attrs['height'] ) / max( 1, $ori_height ) ); } $attrs['width'] = $ori_width; $attrs['height'] = $ori_height; - $new_html = preg_replace('#\s+(width|height)=(["\'])[^\2]*?\2#', '', $match[0]); + $new_html = preg_replace( '#\s+(width|height)=(["\'])[^\2]*?\2#', '', $match[0] ); $new_html = preg_replace( '#<img\s+#i', - '<img width="' . Str::trim_quotes($attrs['width']) . '" height="' . Str::trim_quotes($attrs['height']) . '" ', + '<img width="' . Str::trim_quotes( $attrs['width'] ) . '" height="' . Str::trim_quotes( $attrs['height'] ) . '" ', $new_html ); - self::debug('Add missing sizes ' . $attrs['width'] . 'x' . $attrs['height'] . ' to ' . $attrs['src']); - $this->content = str_replace($match[0], $new_html, $this->content); + self::debug( 'Add missing sizes ' . $attrs['width'] . 'x' . $attrs['height'] . ' to ' . $attrs['src'] ); + $this->content = str_replace( $match[0], $new_html, $this->content ); $match[0] = $new_html; } } } $placeholder = false; - if (!empty($attrs['width']) && $attrs['width'] != 'auto' && !empty($attrs['height']) && $attrs['height'] != 'auto') { - $placeholder = intval($attrs['width']) . 'x' . intval($attrs['height']); + if ( ! empty( $attrs['width'] ) && 'auto' !== $attrs['width'] && ! empty( $attrs['height'] ) && 'auto' !== $attrs['height'] ) { + $placeholder = (int) $attrs['width'] . 'x' . (int) $attrs['height']; } $src_list[] = $attrs['src']; @@ -834,28 +1015,32 @@ private function _parse_img() { } /** - * Detect the original sizes + * Detect the original sizes. + * + * @since 4.0 * - * @since 4.0 + * @param string $src Source URL/path. + * @return array|false getimagesize array or false. */ private function _detect_dimensions( $src ) { - if ($pathinfo = Utility::is_internal_file($src)) { + $pathinfo = Utility::is_internal_file( $src ); + if ( $pathinfo ) { $src = $pathinfo[0]; - } elseif (apply_filters('litespeed_media_ignore_remote_missing_sizes', false)) { + } elseif ( apply_filters( 'litespeed_media_ignore_remote_missing_sizes', false ) ) { return false; } - if (substr($src, 0, 2) == '//') { + if ( 0 === strpos( $src, '//' ) ) { $src = 'https:' . $src; } try { - $sizes = getimagesize($src); - } catch (\Exception $e) { + $sizes = getimagesize( $src ); + } catch ( \Exception $e ) { return false; } - if (!empty($sizes[0]) && !empty($sizes[1])) { + if ( ! empty( $sizes[0] ) && ! empty( $sizes[1] ) ) { return $sizes; } @@ -863,60 +1048,61 @@ private function _detect_dimensions( $src ) { } /** - * Parse iframe src + * Parse iframe src. * * @since 1.4 * @access private - * @return array All the src & related raw html list + * @return array All the related raw html list (full <iframe> tags). */ private function _parse_iframe() { - $cls_excludes = apply_filters('litespeed_media_iframe_lazy_cls_excludes', $this->conf(Base::O_MEDIA_IFRAME_LAZY_CLS_EXC)); + $cls_excludes = apply_filters( 'litespeed_media_iframe_lazy_cls_excludes', $this->conf( Base::O_MEDIA_IFRAME_LAZY_CLS_EXC ) ); $cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427 - $html_list = array(); + $html_list = []; - $content = preg_replace('#<!--.*-->#sU', '', $this->content); + $content = preg_replace( '#<!--.*-->#sU', '', $this->content ); /** - * Exclude parent classes + * Exclude parent classes. * - * @since 3.0 + * @since 3.0 */ - $parent_cls_exc = apply_filters('litespeed_media_iframe_lazy_parent_cls_excludes', $this->conf(Base::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC)); - if ($parent_cls_exc) { - Debug2::debug2('[Media] Iframe Lazyload Class excludes', $parent_cls_exc); - foreach ($parent_cls_exc as $v) { + $parent_cls_exc = apply_filters( 'litespeed_media_iframe_lazy_parent_cls_excludes', $this->conf( Base::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC ) ); + if ( $parent_cls_exc ) { + self::debug2( 'Iframe Lazyload Class excludes', $parent_cls_exc ); + foreach ( $parent_cls_exc as $v ) { $content = preg_replace('#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote($v, '#') . '[^\'"]*\2[^>]*>.*</\1>#sU', '', $content); } } - preg_match_all('#<iframe \s*([^>]+)></iframe>#isU', $content, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $attrs = Utility::parse_attr($match[1]); + preg_match_all( '#<iframe \s*([^>]+)></iframe>#isU', $content, $matches, PREG_SET_ORDER ); + foreach ( $matches as $match ) { + $attrs = Utility::parse_attr( $match[1] ); - if (empty($attrs['src'])) { + if ( empty( $attrs['src'] ) ) { continue; } - Debug2::debug2('[Media] found iframe: ' . $attrs['src']); + self::debug2( 'found iframe: ' . $attrs['src'] ); - if (!empty($attrs['data-no-lazy']) || !empty($attrs['data-skip-lazy']) || !empty($attrs['data-lazyloaded']) || !empty($attrs['data-src'])) { - Debug2::debug2('[Media] bypassed'); + if ( ! empty( $attrs['data-no-lazy'] ) || ! empty( $attrs['data-skip-lazy'] ) || ! empty( $attrs['data-lazyloaded'] ) || ! empty( $attrs['data-src'] ) ) { + self::debug2( 'bypassed' ); continue; } - if (!empty($attrs['class']) && ($hit = Utility::str_hit_array($attrs['class'], $cls_excludes))) { - Debug2::debug2('[Media] iframe lazyload cls excludes [hit] ' . $hit); + $hit = ! empty( $attrs['class'] ) ? Utility::str_hit_array( $attrs['class'], $cls_excludes ) : false; + if ( $hit ) { + self::debug2( 'iframe lazyload cls excludes [hit] ' . $hit ); continue; } - if (apply_filters('litespeed_iframe_lazyload_exc', false, $attrs['src'])) { - Debug2::debug2('[Media] bypassed by filter'); + if ( apply_filters( 'litespeed_iframe_lazyload_exc', false, $attrs['src'] ) ) { + self::debug2( 'bypassed by filter' ); continue; } - // to avoid multiple replacement - if (in_array($match[0], $html_list)) { + // to avoid multiple replacement. + if ( in_array( $match[0], $html_list, true ) ) { continue; } @@ -927,159 +1113,176 @@ private function _parse_iframe() { } /** - * Replace image src to webp + * Replace image src to webp/avif in buffer. * * @since 1.6.2 * @access private + * + * @param string $content HTML content. + * @return string Modified content. */ private function _replace_buffer_img_webp( $content ) { /** - * Added custom element & attribute support + * Added custom element & attribute support. * * @since 2.2.2 */ - $webp_ele_to_check = $this->conf(Base::O_IMG_OPTM_WEBP_ATTR); + $webp_ele_to_check = $this->conf( Base::O_IMG_OPTM_WEBP_ATTR ); - foreach ($webp_ele_to_check as $v) { - if (!$v || strpos($v, '.') === false) { - Debug2::debug2('[Media] buffer_webp no . attribute ' . $v); + foreach ( $webp_ele_to_check as $v ) { + if ( ! $v || false === strpos( $v, '.' ) ) { + self::debug2( 'buffer_webp no . attribute ' . $v ); continue; } - Debug2::debug2('[Media] buffer_webp attribute ' . $v); + self::debug2( 'buffer_webp attribute ' . $v ); - $v = explode('.', $v); - $attr = preg_quote($v[1], '#'); - if ($v[0]) { - $pattern = '#<' . preg_quote($v[0], '#') . '([^>]+)' . $attr . '=([\'"])(.+)\2#iU'; + $v = explode( '.', $v ); + $attr = preg_quote( $v[1], '#' ); + if ( $v[0] ) { + $pattern = '#<' . preg_quote( $v[0], '#' ) . '([^>]+)' . $attr . '=([\'"])(.+)\2#iU'; } else { $pattern = '# ' . $attr . '=([\'"])(.+)\1#iU'; } - preg_match_all($pattern, $content, $matches); + preg_match_all( $pattern, $content, $matches ); - foreach ($matches[$v[0] ? 3 : 2] as $k2 => $url) { - // Check if is a DATA-URI - if (strpos($url, 'data:image') !== false) { + foreach ( $matches[ $v[0] ? 3 : 2 ] as $k2 => $url ) { + // Check if is a DATA-URI. + if ( false !== strpos( $url, 'data:image' ) ) { continue; } - if (!($url2 = $this->replace_webp($url))) { + $url2 = $this->replace_webp( $url ); + if ( ! $url2 ) { continue; } - if ($v[0]) { - $html_snippet = sprintf('<' . $v[0] . '%1$s' . $v[1] . '=%2$s', $matches[1][$k2], $matches[2][$k2] . $url2 . $matches[2][$k2]); + if ( $v[0] ) { + $html_snippet = sprintf( '<' . $v[0] . '%1$s' . $v[1] . '=%2$s', $matches[1][ $k2 ], $matches[2][ $k2 ] . $url2 . $matches[2][ $k2 ] ); } else { - $html_snippet = sprintf(' ' . $v[1] . '=%1$s', $matches[1][$k2] . $url2 . $matches[1][$k2]); + $html_snippet = sprintf( ' ' . $v[1] . '=%1$s', $matches[1][ $k2 ] . $url2 . $matches[1][ $k2 ] ); } - $content = str_replace($matches[0][$k2], $html_snippet, $content); + $content = str_replace( $matches[0][ $k2 ], $html_snippet, $content ); } } - // parse srcset - // todo: should apply this to cdn too - if ((defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_IMG_OPTM_WEBP_REPLACE_SRCSET)) && $this->webp_support()) { - $content = Utility::srcset_replace($content, array( $this, 'replace_webp' )); + // parse srcset. + // todo: should apply this to cdn too. + if ( ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_IMG_OPTM_WEBP_REPLACE_SRCSET ) ) && $this->webp_support() ) { + $content = Utility::srcset_replace( $content, array( $this, 'replace_webp' ) ); } - // Replace background-image - if ((defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_IMG_OPTM_WEBP)) && $this->webp_support()) { - $content = $this->replace_background_webp($content); + // Replace background-image. + if ( ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_IMG_OPTM_WEBP ) ) && $this->webp_support() ) { + $content = $this->replace_background_webp( $content ); } return $content; } /** - * Replace background image + * Replace background image in inline styles and JSON blobs. + * + * @since 4.0 * - * @since 4.0 + * @param string $content HTML content. + * @return string Modified content. */ public function replace_background_webp( $content ) { - Debug2::debug2('[Media] Start replacing background WebP/AVIF.'); + self::debug2( 'Start replacing background WebP/AVIF.' ); - // Handle Elementors data-settings json encode background-images - $content = $this->replace_urls_in_json($content); + // Handle Elementor's data-settings JSON encoded background-images. + $content = $this->replace_urls_in_json( $content ); - // preg_match_all( '#background-image:(\s*)url\((.*)\)#iU', $content, $matches ); - preg_match_all('#url\(([^)]+)\)#iU', $content, $matches); - foreach ($matches[1] as $k => $url) { - // Check if is a DATA-URI - if (strpos($url, 'data:image') !== false) { + preg_match_all( '#url\(([^)]+)\)#iU', $content, $matches ); + foreach ( $matches[1] as $k => $url ) { + // Check if is a DATA-URI. + if ( false !== strpos( $url, 'data:image' ) ) { continue; } /** - * Support quotes in src `background-image: url('src')` + * Support quotes in src `background-image: url('src')`. * * @since 2.9.3 */ - $url = trim($url, '\'"'); + $url = trim( $url, '\'"' ); - // Fix Elementors Slideshow unusual background images like style="background-image: url("https://xxxx.png");" - if (strpos($url, '"') === 0 && substr($url, -6) == '"') { - $url = substr($url, 6, -6); + // Fix Elementor's Slideshow unusual background images like style="background-image: url("https://xxxx.png");" + if ( 0 === strpos( $url, '"' ) && '"' === substr( $url, -6 ) ) { + $url = substr( $url, 6, -6 ); } - if (!($url2 = $this->replace_webp($url))) { + $url2 = $this->replace_webp( $url ); + if ( ! $url2 ) { continue; } - // $html_snippet = sprintf( 'background-image:%1$surl(%2$s)', $matches[ 1 ][ $k ], $url2 ); - $html_snippet = str_replace($url, $url2, $matches[0][$k]); - $content = str_replace($matches[0][$k], $html_snippet, $content); + $html_snippet = str_replace( $url, $url2, $matches[0][ $k ] ); + $content = str_replace( $matches[0][ $k ], $html_snippet, $content ); } return $content; } /** - * Replace images in json data settings attributes + * Replace images in json data settings attributes. * - * @since 6.2 + * @since 6.2 + * + * @param string $content HTML content to scan and modify. + * @return string Modified content with replaced URLs inside JSON attributes. */ public function replace_urls_in_json( $content ) { $pattern = '/data-settings="(.*?)"/i'; $parent_class = $this; - preg_match_all($pattern, $content, $matches, PREG_SET_ORDER); + preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER ); - foreach ($matches as $match) { - // Check if the string contains HTML entities - $isEncoded = preg_match('/"|<|>|&|'/', $match[1]); + foreach ( $matches as $match ) { + // Check if the string contains HTML entities. + $is_encoded = preg_match( '/"|<|>|&|'/', $match[1] ); - // Decode HTML entities in the JSON string - $jsonString = html_entity_decode($match[1]); + // Decode HTML entities in the JSON string. + $json_string = html_entity_decode( $match[1] ); - $jsonData = \json_decode($jsonString, true); + $json_data = \json_decode( $json_string, true ); - if (json_last_error() === JSON_ERROR_NONE) { + if ( JSON_ERROR_NONE === json_last_error() && is_array( $json_data ) ) { $did_webp_replace = false; - array_walk_recursive($jsonData, function ( &$item, $key ) use ( &$did_webp_replace, $parent_class ) { - if ($key == 'url') { - $item_image = $parent_class->replace_webp($item); - if ($item_image) { - $item = $item_image; - - $did_webp_replace = true; + array_walk_recursive( + $json_data, + /** + * Replace URLs in JSON data recursively. + * + * @param mixed $item Value (modified in place). + * @param string $key Array key. + */ + function ( &$item, $key ) use ( &$did_webp_replace, $parent_class ) { + if ( 'url' === $key ) { + $item_image = $parent_class->replace_webp( $item ); + if ( $item_image ) { + $item = $item_image; + $did_webp_replace = true; + } } } - }); + ); - if ($did_webp_replace) { - // Re-encode the modified array back to a JSON string - $newJsonString = \json_encode($jsonData); + if ( $did_webp_replace ) { + // Re-encode the modified array back to a JSON string. + $new_json_string = wp_json_encode( $json_data ); - // Re-encode the JSON string to HTML entities only if it was originally encoded - if ($isEncoded) { - $newJsonString = htmlspecialchars($newJsonString, ENT_QUOTES | 0); // ENT_HTML401 is for PHPv5.4+ + // Re-encode the JSON string to HTML entities only if it was originally encoded. + if ( $is_encoded ) { + $new_json_string = htmlspecialchars( $new_json_string, ENT_QUOTES | 0 ); // ENT_HTML401 for PHP>=5.4. } - // Replace the old JSON string in the content with the new, modified JSON string - $content = str_replace($match[1], $newJsonString, $content); + // Replace the old JSON string in the content with the new, modified JSON string. + $content = str_replace( $match[1], $new_json_string, $content ); } } } @@ -1088,94 +1291,105 @@ public function replace_urls_in_json( $content ) { } /** - * Replace internal image src to webp or avif + * Replace internal image src to webp or avif. * * @since 1.6.2 * @access public + * + * @param string $url Image URL. + * @return string|false Replaced URL or false if not applicable. */ public function replace_webp( $url ) { - if (!$this->webp_support()) { - self::debug2('No next generation format chosen in setting, bypassed'); + if ( ! $this->webp_support() ) { + self::debug2( 'No next generation format chosen in setting, bypassed' ); return false; } - Debug2::debug2('[Media] ' . $this->_sys_format . ' replacing: ' . substr($url, 0, 200)); + self::debug2( $this->_sys_format . ' replacing: ' . substr( $url, 0, 200 ) ); - if (substr($url, -5) === '.' . $this->_sys_format) { - Debug2::debug2('[Media] already ' . $this->_sys_format); + if ( substr( $url, -5 ) === '.' . $this->_sys_format ) { + self::debug2( 'already ' . $this->_sys_format ); return false; } /** - * WebP API hook - * NOTE: As $url may contain query strings, WebP check will need to parse_url before appending .webp + * WebP/AVIF API hook. + * NOTE: As $url may contain query strings, check filters which may parse_url before appending format. * * @since 2.9.5 * @see #751737 - API docs for WebP generation */ - if (apply_filters('litespeed_media_check_ori', Utility::is_internal_file($url), $url)) { - // check if has webp file - if (apply_filters('litespeed_media_check_webp', Utility::is_internal_file($url, $this->_sys_format), $url)) { + $ori_check = apply_filters( 'litespeed_media_check_ori', Utility::is_internal_file( $url ), $url ); + if ( $ori_check ) { + // check if has webp/avif file. + $has_next = apply_filters( 'litespeed_media_check_webp', Utility::is_internal_file( $url, $this->_sys_format ), $url ); + if ( $has_next ) { $url .= '.' . $this->_sys_format; } else { - Debug2::debug2('[Media] -no WebP or AVIF file, bypassed'); + self::debug2( '-no WebP or AVIF file, bypassed' ); return false; } } else { - Debug2::debug2('[Media] -no file, bypassed'); + self::debug2( '-no file, bypassed' ); return false; } - Debug2::debug2('[Media] - replaced to: ' . $url); + self::debug2( '- replaced to: ' . $url ); return $url; } /** - * Hook to wp_get_attachment_image_src + * Hook to wp_get_attachment_image_src. * * @since 1.6.2 * @access public - * @param array $img The URL of the attachment image src, the width, the height + * + * @param array $img The URL, width, height array. * @return array */ public function webp_attach_img_src( $img ) { - Debug2::debug2('[Media] changing attach src: ' . $img[0]); - if ($img && ($url = $this->replace_webp($img[0]))) { + self::debug2( 'changing attach src: ' . $img[0] ); + $url = $img ? $this->replace_webp( $img[0] ) : false; + if ( $url ) { $img[0] = $url; } return $img; } /** - * Try to replace img url + * Try to replace img url. * * @since 1.6.2 * @access public - * @param string $url + * + * @param string $url Image URL. * @return string */ public function webp_url( $url ) { - if ($url && ($url2 = $this->replace_webp($url))) { + $url2 = $url ? $this->replace_webp( $url ) : false; + if ( $url2 ) { $url = $url2; } return $url; } /** - * Hook to replace WP responsive images + * Hook to replace WP responsive images. * * @since 1.6.2 * @access public - * @param array $srcs + * + * @param array $srcs Srcset array. * @return array */ public function webp_srcset( $srcs ) { - if ($srcs) { - foreach ($srcs as $w => $data) { - if (!($url = $this->replace_webp($data['url']))) { + if ( $srcs ) { + foreach ( $srcs as $w => $data ) { + $url = $this->replace_webp( $data['url'] ); + if ( ! $url ) { continue; } - $srcs[$w]['url'] = $url; + $srcs[ $w ]['url'] = $url; } } return $srcs; diff --git a/src/metabox.cls.php b/src/metabox.cls.php index 577645b43..816049262 100644 --- a/src/metabox.cls.php +++ b/src/metabox.cls.php @@ -1,11 +1,10 @@ <?php +// phpcs:ignoreFile /** * The class to operate post editor metabox settings * * @since 4.7 - * @package Core - * @subpackage Core/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> + * @package LiteSpeed */ namespace LiteSpeed; diff --git a/src/object-cache-wp.cls.php b/src/object-cache-wp.cls.php new file mode 100644 index 000000000..a0725d345 --- /dev/null +++ b/src/object-cache-wp.cls.php @@ -0,0 +1,981 @@ +<?php +/** + * WP Object Cache wrapper for LiteSpeed Cache. + * + * Provides a drop-in-compatible object cache implementation that proxies to + * LiteSpeed's persistent cache while keeping a local runtime cache. + * + * @package LiteSpeed + * @since 1.8 + */ + +/** + * Class WP_Object_Cache + * + * Implements the WordPress object cache for LiteSpeed Cache. + * + * @since 1.8 + * @package LiteSpeed + */ +class WP_Object_Cache { + + /** + * Singleton instance + * + * @since 1.8 + * @access protected + * @var WP_Object_Cache|null + */ + protected static $_instance; + + /** + * Object cache instance + * + * @since 1.8 + * @access private + * @var \LiteSpeed\Object_Cache + */ + private $_object_cache; + + /** + * Cache storage + * + * @since 1.8 + * @access private + * @var array + */ + private $_cache = array(); + + /** + * Cache for 404 keys + * + * @since 1.8 + * @access private + * @var array + */ + private $_cache_404 = array(); + + /** + * Total cache operations + * + * @since 1.8 + * @access private + * @var int + */ + private $cache_total = 0; + + /** + * Cache hits within call + * + * @since 1.8 + * @access private + * @var int + */ + private $count_hit_incall = 0; + + /** + * Cache hits + * + * @since 1.8 + * @access private + * @var int + */ + private $count_hit = 0; + + /** + * Cache misses within call + * + * @since 1.8 + * @access private + * @var int + */ + private $count_miss_incall = 0; + + /** + * Cache misses + * + * @since 1.8 + * @access private + * @var int + */ + private $count_miss = 0; + + /** + * Cache set operations + * + * @since 1.8 + * @access private + * @var int + */ + private $count_set = 0; + + /** + * Global cache groups + * + * @since 1.8 + * @access protected + * @var array + */ + protected $global_groups = array(); + + /** + * Blog prefix for cache keys + * + * @since 1.8 + * @access private + * @var string + */ + private $blog_prefix; + + /** + * Multisite flag + * + * @since 1.8 + * @access private + * @var bool + */ + private $multisite; + + /** + * Init. + * + * Initializes the object cache with LiteSpeed settings. + * + * @since 1.8 + * @access public + */ + public function __construct() { + $this->_object_cache = \LiteSpeed\Object_Cache::cls(); + + $this->multisite = is_multisite(); + $this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : ''; + + /** + * Fix multiple instance using same oc issue + * + * @since 1.8.2 + */ + if ( ! defined( 'LSOC_PREFIX' ) ) { + define( 'LSOC_PREFIX', substr( md5( __FILE__ ), -5 ) ); + } + } + + /** + * Makes private properties readable for backward compatibility. + * + * @since 5.4 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Makes private properties settable for backward compatibility. + * + * @since 5.4 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + $this->$name = $value; + + return $this->$name; + } + + /** + * Makes private properties checkable for backward compatibility. + * + * @since 5.4 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Makes private properties un-settable for backward compatibility. + * + * @since 5.4 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Serves as a utility function to determine whether a key is valid. + * + * @since 5.4 + * @access protected + * + * @param int|string $key Cache key to check for validity. + * @return bool Whether the key is valid. + */ + protected function is_valid_key( $key ) { + if ( is_int( $key ) ) { + return true; + } + + if ( is_string( $key ) && '' !== trim( $key ) ) { + return true; + } + + $type = gettype( $key ); + + if ( ! function_exists( '__' ) ) { + wp_load_translations_early(); + } + + $message = is_string( $key ) + ? __( 'Cache key must not be an empty string.' ) + : sprintf( + /* translators: %s: The type of the given cache key. */ + __( 'Cache key must be integer or non-empty string, %s given.' ), + $type + ); + + _doing_it_wrong( + esc_html( __METHOD__ ), + esc_html( $message ), + '6.1.0' + ); + + return false; + } + + /** + * Get the final key. + * + * Generates a unique cache key based on group and prefix. + * + * @since 1.8 + * @access private + * @param int|string $key Cache key. + * @param string $group Optional. Cache group. Default 'default'. + * @return string The final cache key. + */ + private function _key( $key, $group = 'default' ) { + if ( empty( $group ) ) { + $group = 'default'; + } + + $prefix = $this->_object_cache->is_global( $group ) ? '' : $this->blog_prefix; + + return LSOC_PREFIX . $prefix . $group . '.' . $key; + } + + /** + * Output debug info. + * + * Returns cache statistics for debugging purposes. + * + * @since 1.8 + * @access public + * @return string Cache statistics. + */ + public function debug() { + return ' [total] ' . + $this->cache_total . + ' [hit_incall] ' . + $this->count_hit_incall . + ' [hit] ' . + $this->count_hit . + ' [miss_incall] ' . + $this->count_miss_incall . + ' [miss] ' . + $this->count_miss . + ' [set] ' . + $this->count_set; + } + + /** + * Adds data to the cache if it doesn't already exist. + * + * @since 1.8 + * @access public + * @see WP_Object_Cache::set() + * + * @param int|string $key What to call the contents in the cache. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Default 'default'. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True on success, false if cache key and group already exist. + */ + public function add( $key, $data, $group = 'default', $expire = 0 ) { + if ( wp_suspend_cache_addition() ) { + return false; + } + + if ( ! $this->is_valid_key( $key ) ) { + return false; + } + + if ( empty( $group ) ) { + $group = 'default'; + } + + $id = $this->_key( $key, $group ); + + if ( array_key_exists( $id, $this->_cache ) ) { + return false; + } + + return $this->set( $key, $data, $group, (int) $expire ); + } + + /** + * Adds multiple values to the cache in one call. + * + * @since 5.4 + * @access public + * + * @param array $data Array of keys and values to be added. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false if cache key and group already exist. + */ + public function add_multiple( array $data, $group = '', $expire = 0 ) { + $values = array(); + + foreach ( $data as $key => $value ) { + $values[ $key ] = $this->add( $key, $value, $group, $expire ); + } + + return $values; + } + + /** + * Replaces the contents in the cache, if contents already exist. + * + * @since 1.8 + * @access public + * @see WP_Object_Cache::set() + * + * @param int|string $key What to call the contents in the cache. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Default 'default'. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True if contents were replaced, false if original value does not exist. + */ + public function replace( $key, $data, $group = 'default', $expire = 0 ) { + if ( ! $this->is_valid_key( $key ) ) { + return false; + } + + if ( empty( $group ) ) { + $group = 'default'; + } + + $id = $this->_key( $key, $group ); + + if ( ! array_key_exists( $id, $this->_cache ) ) { + return false; + } + + return $this->set( $key, $data, $group, (int) $expire ); + } + + /** + * Sets the data contents into the cache. + * + * The cache contents are grouped by the $group parameter followed by the + * $key. This allows for duplicate IDs in unique groups. Therefore, naming of + * the group should be used with care and should follow normal function + * naming guidelines outside of core WordPress usage. + * + * The $expire parameter is not used, because the cache will automatically + * expire for each time a page is accessed and PHP finishes. The method is + * more for cache plugins which use files. + * + * @since 1.8 + * @since 5.4 Returns false if cache key is invalid. + * @access public + * + * @param int|string $key What to call the contents in the cache. + * @param mixed $data The contents to store in the cache. + * @param string $group Optional. Where to group the cache contents. Default 'default'. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool True if contents were set, false if key is invalid. + */ + public function set( $key, $data, $group = 'default', $expire = 0 ) { + if ( ! $this->is_valid_key( $key ) ) { + return false; + } + + if ( empty( $group ) ) { + $group = 'default'; + } + + $id = $this->_key( $key, $group ); + + if ( is_object( $data ) ) { + $data = clone $data; + } + + $this->_cache[ $id ] = $data; + + if ( array_key_exists( $id, $this->_cache_404 ) ) { + unset( $this->_cache_404[ $id ] ); + } + + if ( ! $this->_object_cache->is_non_persistent( $group ) ) { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize + $this->_object_cache->set( $id, serialize( array( 'data' => $data ) ), (int) $expire ); + ++$this->count_set; + } + + if ( $this->_object_cache->store_transients( $group ) ) { + $this->_transient_set( $key, $data, $group, (int) $expire ); + } + + return true; + } + + /** + * Sets multiple values to the cache in one call. + * + * @since 5.4 + * @access public + * + * @param array $data Array of key and value to be set. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Each value is always true. + */ + public function set_multiple( array $data, $group = '', $expire = 0 ) { + $values = array(); + + foreach ( $data as $key => $value ) { + $values[ $key ] = $this->set( $key, $value, $group, $expire ); + } + + return $values; + } + + /** + * Retrieves the cache contents, if it exists. + * + * The contents will be first attempted to be retrieved by searching by the + * key in the cache group. If the cache is hit (success) then the contents + * are returned. + * + * On failure, the number of cache misses will be incremented. + * + * @since 1.8 + * @access public + * + * @param int|string $key The key under which the cache contents are stored. + * @param string $group Optional. Where the cache contents are grouped. Default 'default'. + * @param bool $force Optional. Unused. Whether to force an update of the local cache + * from the persistent cache. Default false. + * @param bool $found Optional. Whether the key was found in the cache (passed by reference). + * Disambiguates a return of false, a storable value. Default null. + * @return mixed|false The cache contents on success, false on failure to retrieve contents. + */ + public function get( $key, $group = 'default', $force = false, &$found = null ) { + if ( ! $this->is_valid_key( $key ) ) { + return false; + } + + if ( empty( $group ) ) { + $group = 'default'; + } + + $id = $this->_key( $key, $group ); + + $found = false; + $found_in_oc = false; + $cache_val = false; + + if ( array_key_exists( $id, $this->_cache ) && ! $force ) { + $found = true; + $cache_val = $this->_cache[ $id ]; + ++$this->count_hit_incall; + } elseif ( ! array_key_exists( $id, $this->_cache_404 ) && ! $this->_object_cache->is_non_persistent( $group ) ) { + $v = $this->_object_cache->get( $id ); + + if ( null !== $v ) { + $v = maybe_unserialize( $v ); + } + + // To be compatible with false val. + if ( is_array( $v ) && array_key_exists( 'data', $v ) ) { + ++$this->count_hit; + $found = true; + $found_in_oc = true; + $cache_val = $v['data']; + } else { + // Can't find key, cache it to 404. + $this->_cache_404[ $id ] = 1; + ++$this->count_miss; + } + } else { + ++$this->count_miss_incall; + } + + if ( is_object( $cache_val ) ) { + $cache_val = clone $cache_val; + } + + // If not found but has `Store Transients` cfg on, still need to follow WP's get_transient() logic. + if ( ! $found && $this->_object_cache->store_transients( $group ) ) { + $cache_val = $this->_transient_get( $key, $group ); + if ( $cache_val ) { + $found = true; + } + } + + if ( $found_in_oc ) { + $this->_cache[ $id ] = $cache_val; + } + + ++$this->cache_total; + + return $cache_val; + } + + /** + * Retrieves multiple values from the cache in one call. + * + * @since 5.4 + * @access public + * + * @param array $keys Array of keys under which the cache contents are stored. + * @param string $group Optional. Where the cache contents are grouped. Default 'default'. + * @param bool $force Optional. Whether to force an update of the local cache + * from the persistent cache. Default false. + * @return array Array of return values, grouped by key. Each value is either + * the cache contents on success, or false on failure. + */ + public function get_multiple( $keys, $group = 'default', $force = false ) { + $values = array(); + + foreach ( $keys as $key ) { + $values[ $key ] = $this->get( $key, $group, $force ); + } + + return $values; + } + + /** + * Removes the contents of the cache key in the group. + * + * If the cache key does not exist in the group, then nothing will happen. + * + * @since 1.8 + * @access public + * + * @param int|string $key What the contents in the cache are called. + * @param string $group Optional. Where the cache contents are grouped. Default 'default'. + * @return bool True on success, false if the contents were not deleted. + */ + public function delete( $key, $group = 'default' ) { + if ( ! $this->is_valid_key( $key ) ) { + return false; + } + + if ( empty( $group ) ) { + $group = 'default'; + } + + $id = $this->_key( $key, $group ); + + if ( $this->_object_cache->store_transients( $group ) ) { + $this->_transient_del( $key, $group ); + } + + if ( array_key_exists( $id, $this->_cache ) ) { + unset( $this->_cache[ $id ] ); + } + + if ( $this->_object_cache->is_non_persistent( $group ) ) { + return false; + } + + return $this->_object_cache->delete( $id ); + } + + /** + * Deletes multiple values from the cache in one call. + * + * @since 5.4 + * @access public + * + * @param array $keys Array of keys to be deleted. + * @param string $group Optional. Where the cache contents are grouped. Default empty. + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false if the contents were not deleted. + */ + public function delete_multiple( array $keys, $group = '' ) { + $values = array(); + + foreach ( $keys as $key ) { + $values[ $key ] = $this->delete( $key, $group ); + } + + return $values; + } + + /** + * Increments numeric cache item's value. + * + * @since 5.4 + * @access public + * + * @param int|string $key The cache key to increment. + * @param int $offset Optional. The amount by which to increment the item's value. + * Default 1. + * @param string $group Optional. The group the key is in. Default 'default'. + * @return int|false The item's new value on success, false on failure. + */ + public function incr( $key, $offset = 1, $group = 'default' ) { + return $this->incr_desr( $key, $offset, $group, true ); + } + + /** + * Decrements numeric cache item's value. + * + * @since 5.4 + * @access public + * + * @param int|string $key The cache key to decrement. + * @param int $offset Optional. The amount by which to decrement the item's value. + * Default 1. + * @param string $group Optional. The group the key is in. Default 'default'. + * @return int|false The item's new value on success, false on failure. + */ + public function decr( $key, $offset = 1, $group = 'default' ) { + return $this->incr_desr( $key, $offset, $group, false ); + } + + /** + * Increments or decrements numeric cache item's value. + * + * @since 1.8 + * @access public + * + * @param int|string $key The cache key to increment or decrement. + * @param int $offset The amount by which to adjust the item's value. + * @param string $group Optional. The group the key is in. Default 'default'. + * @param bool $incr True to increment, false to decrement. + * @return int|false The item's new value on success, false on failure. + */ + public function incr_desr( $key, $offset = 1, $group = 'default', $incr = true ) { + if ( ! $this->is_valid_key( $key ) ) { + return false; + } + + if ( empty( $group ) ) { + $group = 'default'; + } + + $cache_val = $this->get( $key, $group ); + + if ( false === $cache_val ) { + return false; + } + + if ( ! is_numeric( $cache_val ) ) { + $cache_val = 0; + } + + $offset = (int) $offset; + + if ( $incr ) { + $cache_val += $offset; + } else { + $cache_val -= $offset; + } + + if ( $cache_val < 0 ) { + $cache_val = 0; + } + + $this->set( $key, $cache_val, $group ); + + return $cache_val; + } + + /** + * Clears the object cache of all data. + * + * @since 1.8 + * @access public + * + * @return true Always returns true. + */ + public function flush() { + $this->flush_runtime(); + + $this->_object_cache->flush(); + + return true; + } + + /** + * Removes all cache items from the in-memory runtime cache. + * + * @since 5.4 + * @access public + * + * @return true Always returns true. + */ + public function flush_runtime() { + $this->_cache = array(); + $this->_cache_404 = array(); + + return true; + } + + /** + * Removes all cache items in a group. + * + * @since 5.4 + * @access public + * + * @return true Always returns true. + */ + public function flush_group() { + return true; + } + + /** + * Sets the list of global cache groups. + * + * @since 1.8 + * @access public + * + * @param string|string[] $groups List of groups that are global. + */ + public function add_global_groups( $groups ) { + $groups = (array) $groups; + + $this->_object_cache->add_global_groups( $groups ); + } + + /** + * Sets the list of non-persistent cache groups. + * + * @since 1.8 + * @access public + * + * @param string|string[] $groups A group or an array of groups to add. + */ + public function add_non_persistent_groups( $groups ) { + $groups = (array) $groups; + + $this->_object_cache->add_non_persistent_groups( $groups ); + } + + /** + * Switches the internal blog ID. + * + * This changes the blog ID used to create keys in blog specific groups. + * + * @since 1.8 + * @access public + * + * @param int $blog_id Blog ID. + */ + public function switch_to_blog( $blog_id ) { + $blog_id = (int) $blog_id; + $this->blog_prefix = $this->multisite ? $blog_id . ':' : ''; + } + + /** + * Get transient from wp table + * + * Retrieves transient data from WordPress options table. + * + * @since 1.8.3 + * @access private + * @see `wp-includes/option.php` function `get_transient`/`set_site_transient` + * + * @param string $transient Transient name. + * @param string $group Transient group ('transient' or 'site-transient'). + * @return mixed Transient value or false if not found. + */ + private function _transient_get( $transient, $group ) { + if ( 'transient' === $group ) { + /**** Ori WP func start */ + $transient_option = '_transient_' . $transient; + if ( ! wp_installing() ) { + // If option is not in alloptions, it is not autoloaded and thus has a timeout + $alloptions = wp_load_alloptions(); + if ( ! isset( $alloptions[ $transient_option ] ) ) { + $transient_timeout = '_transient_timeout_' . $transient; + $timeout = get_option( $transient_timeout ); + if ( false !== $timeout && $timeout < time() ) { + delete_option( $transient_option ); + delete_option( $transient_timeout ); + $value = false; + } + } + } + + if ( ! isset( $value ) ) { + $value = get_option( $transient_option ); + } + /**** Ori WP func end */ + } elseif ( 'site-transient' === $group ) { + /**** Ori WP func start */ + $no_timeout = array( 'update_core', 'update_plugins', 'update_themes' ); + $transient_option = '_site_transient_' . $transient; + if ( ! in_array( $transient, $no_timeout, true ) ) { + $transient_timeout = '_site_transient_timeout_' . $transient; + $timeout = get_site_option( $transient_timeout ); + if ( false !== $timeout && $timeout < time() ) { + delete_site_option( $transient_option ); + delete_site_option( $transient_timeout ); + $value = false; + } + } + + if ( ! isset( $value ) ) { + $value = get_site_option( $transient_option ); + } + /**** Ori WP func end */ + } else { + $value = false; + } + + return $value; + } + + /** + * Set transient to WP table + * + * Stores transient data in WordPress options table. + * + * @since 1.8.3 + * @access private + * @see `wp-includes/option.php` function `set_transient`/`set_site_transient` + * + * @param string $transient Transient name. + * @param mixed $value Transient value. + * @param string $group Transient group ('transient' or 'site-transient'). + * @param int $expiration Time until expiration in seconds. + * @return bool True on success, false on failure. + */ + private function _transient_set( $transient, $value, $group, $expiration ) { + if ( 'transient' === $group ) { + /**** Ori WP func start */ + $transient_timeout = '_transient_timeout_' . $transient; + $transient_option = '_transient_' . $transient; + if ( false === get_option( $transient_option ) ) { + $autoload = 'yes'; + if ( (int) $expiration ) { + $autoload = 'no'; + add_option( $transient_timeout, time() + (int) $expiration, '', 'no' ); + } + $result = add_option( $transient_option, $value, '', $autoload ); + } else { + // If expiration is requested, but the transient has no timeout option, + // delete, then re-create transient rather than update. + $update = true; + if ( (int) $expiration ) { + if ( false === get_option( $transient_timeout ) ) { + delete_option( $transient_option ); + add_option( $transient_timeout, time() + (int) $expiration, '', 'no' ); + $result = add_option( $transient_option, $value, '', 'no' ); + $update = false; + } else { + update_option( $transient_timeout, time() + (int) $expiration ); + } + } + if ( $update ) { + $result = update_option( $transient_option, $value ); + } + } + /**** Ori WP func end */ + } elseif ( 'site-transient' === $group ) { + /**** Ori WP func start */ + $transient_timeout = '_site_transient_timeout_' . $transient; + $option = '_site_transient_' . $transient; + if ( false === get_site_option( $option ) ) { + if ( (int) $expiration ) { + add_site_option( $transient_timeout, time() + (int) $expiration ); + } + $result = add_site_option( $option, $value ); + } else { + if ( (int) $expiration ) { + update_site_option( $transient_timeout, time() + (int) $expiration ); + } + $result = update_site_option( $option, $value ); + } + /**** Ori WP func end */ + } else { + $result = null; + } + + return $result; + } + + /** + * Delete transient from WP table + * + * Removes transient data from WordPress options table. + * + * @since 1.8.3 + * @access private + * @see `wp-includes/option.php` function `delete_transient`/`delete_site_transient` + * + * @param string $transient Transient name. + * @param string $group Transient group ('transient' or 'site-transient'). + */ + private function _transient_del( $transient, $group ) { + if ( 'transient' === $group ) { + /**** Ori WP func start */ + $option_timeout = '_transient_timeout_' . $transient; + $option = '_transient_' . $transient; + $result = delete_option( $option ); + if ( $result ) { + delete_option( $option_timeout ); + } + /**** Ori WP func end */ + } elseif ( 'site-transient' === $group ) { + /**** Ori WP func start */ + $option_timeout = '_site_transient_timeout_' . $transient; + $option = '_site_transient_' . $transient; + $result = delete_site_option( $option ); + if ( $result ) { + delete_site_option( $option_timeout ); + } + /**** Ori WP func end */ + } + } + + /** + * Get the current instance object. + * + * @since 1.8 + * @access public + * + * @return WP_Object_Cache The current instance. + */ + public static function get_instance() { + if ( ! isset( self::$_instance ) ) { + self::$_instance = new self(); + } + + return self::$_instance; + } +} diff --git a/src/object-cache.cls.php b/src/object-cache.cls.php index 73fe85c68..351d6df38 100644 --- a/src/object-cache.cls.php +++ b/src/object-cache.cls.php @@ -1,134 +1,321 @@ <?php - /** - * The object cache class + * The object cache class. * * @since 1.8 * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); -require_once dirname(__DIR__) . '/autoload.php'; +require_once dirname( __DIR__ ) . '/autoload.php'; +/** + * Object cache handler using Redis or Memcached. + * + * NOTE: this class may be included without initialized core. + * + * @since 1.8 + */ class Object_Cache extends Root { + const LOG_TAG = '[Object_Cache]'; + + /** + * Debug option key. + * + * @var string + */ + const O_DEBUG = 'debug'; + + /** + * Object cache enable key. + * + * @var string + */ + const O_OBJECT = 'object'; + + /** + * Object kind (Redis/Memcached). + * + * @var string + */ + const O_OBJECT_KIND = 'object-kind'; + + /** + * Object host. + * + * @var string + */ + const O_OBJECT_HOST = 'object-host'; + + /** + * Object port. + * + * @var string + */ + const O_OBJECT_PORT = 'object-port'; + + /** + * Object life/TTL. + * + * @var string + */ + const O_OBJECT_LIFE = 'object-life'; + + /** + * Persistent connection flag. + * + * @var string + */ + const O_OBJECT_PERSISTENT = 'object-persistent'; + + /** + * Admin cache flag. + * + * @var string + */ + const O_OBJECT_ADMIN = 'object-admin'; + + /** + * Transients store flag. + * + * @var string + */ + const O_OBJECT_TRANSIENTS = 'object-transients'; + + /** + * DB index for Redis. + * + * @var string + */ + const O_OBJECT_DB_ID = 'object-db_id'; + + /** + * Username for auth. + * + * @var string + */ + const O_OBJECT_USER = 'object-user'; + + /** + * Password for auth. + * + * @var string + */ + const O_OBJECT_PSWD = 'object-pswd'; + + /** + * Global groups list. + * + * @var string + */ + const O_OBJECT_GLOBAL_GROUPS = 'object-global_groups'; - const O_DEBUG = 'debug'; - const O_OBJECT = 'object'; - const O_OBJECT_KIND = 'object-kind'; - const O_OBJECT_HOST = 'object-host'; - const O_OBJECT_PORT = 'object-port'; - const O_OBJECT_LIFE = 'object-life'; - const O_OBJECT_PERSISTENT = 'object-persistent'; - const O_OBJECT_ADMIN = 'object-admin'; - const O_OBJECT_TRANSIENTS = 'object-transients'; - const O_OBJECT_DB_ID = 'object-db_id'; - const O_OBJECT_USER = 'object-user'; - const O_OBJECT_PSWD = 'object-pswd'; - const O_OBJECT_GLOBAL_GROUPS = 'object-global_groups'; + /** + * Non-persistent groups list. + * + * @var string + */ const O_OBJECT_NON_PERSISTENT_GROUPS = 'object-non_persistent_groups'; + /** + * Connection instance. + * + * @var \Redis|\Memcached|null + */ private $_conn; + + /** + * Debug config. + * + * @var bool + */ private $_cfg_debug; + + /** + * Whether OC is enabled. + * + * @var bool + */ private $_cfg_enabled; + + /** + * True => Redis, false => Memcached. + * + * @var bool + */ private $_cfg_method; + + /** + * Host name. + * + * @var string + */ private $_cfg_host; + + /** + * Port number. + * + * @var int|string + */ private $_cfg_port; + + /** + * TTL in seconds. + * + * @var int + */ private $_cfg_life; + + /** + * Use persistent connection. + * + * @var bool + */ private $_cfg_persistent; + + /** + * Cache admin pages. + * + * @var bool + */ private $_cfg_admin; + + /** + * Store transients. + * + * @var bool + */ private $_cfg_transients; + + /** + * Redis DB index. + * + * @var int + */ private $_cfg_db; + + /** + * Auth username. + * + * @var string + */ private $_cfg_user; + + /** + * Auth password. + * + * @var string + */ private $_cfg_pswd; + + /** + * Default TTL in seconds. + * + * @var int + */ private $_default_life = 360; - private $_oc_driver = 'Memcached'; // Redis or Memcached + /** + * 'Redis' or 'Memcached'. + * + * @var string + */ + private $_oc_driver = 'Memcached'; // Redis or Memcached. + + /** + * Global groups. + * + * @var array + */ + private $_global_groups = array(); - private $_global_groups = array(); + /** + * Non-persistent groups. + * + * @var array + */ private $_non_persistent_groups = array(); /** - * Init + * Init. * - * NOTE: this class may be included without initialized core + * NOTE: this class may be included without initialized core. * * @since 1.8 + * + * @param array|false $cfg Optional configuration to bootstrap without core. */ public function __construct( $cfg = false ) { - if ($cfg) { - if (!is_array($cfg[Base::O_OBJECT_GLOBAL_GROUPS])) { - $cfg[Base::O_OBJECT_GLOBAL_GROUPS] = explode("\n", $cfg[Base::O_OBJECT_GLOBAL_GROUPS]); + if ( $cfg ) { + if ( ! is_array( $cfg[ Base::O_OBJECT_GLOBAL_GROUPS ] ) ) { + $cfg[ Base::O_OBJECT_GLOBAL_GROUPS ] = explode( "\n", $cfg[ Base::O_OBJECT_GLOBAL_GROUPS ] ); } - if (!is_array($cfg[Base::O_OBJECT_NON_PERSISTENT_GROUPS])) { - $cfg[Base::O_OBJECT_NON_PERSISTENT_GROUPS] = explode("\n", $cfg[Base::O_OBJECT_NON_PERSISTENT_GROUPS]); + if ( ! is_array( $cfg[ Base::O_OBJECT_NON_PERSISTENT_GROUPS ] ) ) { + $cfg[ Base::O_OBJECT_NON_PERSISTENT_GROUPS ] = explode( "\n", $cfg[ Base::O_OBJECT_NON_PERSISTENT_GROUPS ] ); } - $this->_cfg_debug = $cfg[Base::O_DEBUG] ? $cfg[Base::O_DEBUG] : false; - $this->_cfg_method = $cfg[Base::O_OBJECT_KIND] ? true : false; - $this->_cfg_host = $cfg[Base::O_OBJECT_HOST]; - $this->_cfg_port = $cfg[Base::O_OBJECT_PORT]; - $this->_cfg_life = $cfg[Base::O_OBJECT_LIFE]; - $this->_cfg_persistent = $cfg[Base::O_OBJECT_PERSISTENT]; - $this->_cfg_admin = $cfg[Base::O_OBJECT_ADMIN]; - $this->_cfg_transients = $cfg[Base::O_OBJECT_TRANSIENTS]; - $this->_cfg_db = $cfg[Base::O_OBJECT_DB_ID]; - $this->_cfg_user = $cfg[Base::O_OBJECT_USER]; - $this->_cfg_pswd = $cfg[Base::O_OBJECT_PSWD]; - $this->_global_groups = $cfg[Base::O_OBJECT_GLOBAL_GROUPS]; - $this->_non_persistent_groups = $cfg[Base::O_OBJECT_NON_PERSISTENT_GROUPS]; - - if ($this->_cfg_method) { + $this->_cfg_debug = $cfg[ Base::O_DEBUG ] ? $cfg[ Base::O_DEBUG ] : false; + $this->_cfg_method = $cfg[ Base::O_OBJECT_KIND ] ? true : false; + $this->_cfg_host = $cfg[ Base::O_OBJECT_HOST ]; + $this->_cfg_port = $cfg[ Base::O_OBJECT_PORT ]; + $this->_cfg_life = $cfg[ Base::O_OBJECT_LIFE ]; + $this->_cfg_persistent = $cfg[ Base::O_OBJECT_PERSISTENT ]; + $this->_cfg_admin = $cfg[ Base::O_OBJECT_ADMIN ]; + $this->_cfg_transients = $cfg[ Base::O_OBJECT_TRANSIENTS ]; + $this->_cfg_db = $cfg[ Base::O_OBJECT_DB_ID ]; + $this->_cfg_user = $cfg[ Base::O_OBJECT_USER ]; + $this->_cfg_pswd = $cfg[ Base::O_OBJECT_PSWD ]; + $this->_global_groups = $cfg[ Base::O_OBJECT_GLOBAL_GROUPS ]; + $this->_non_persistent_groups = $cfg[ Base::O_OBJECT_NON_PERSISTENT_GROUPS ]; + + if ( $this->_cfg_method ) { $this->_oc_driver = 'Redis'; } - $this->_cfg_enabled = $cfg[Base::O_OBJECT] && class_exists($this->_oc_driver) && $this->_cfg_host; - } - // If OC is OFF, will hit here to init OC after conf initialized - elseif (defined('LITESPEED_CONF_LOADED')) { - $this->_cfg_debug = $this->conf(Base::O_DEBUG) ? $this->conf(Base::O_DEBUG) : false; - $this->_cfg_method = $this->conf(Base::O_OBJECT_KIND) ? true : false; - $this->_cfg_host = $this->conf(Base::O_OBJECT_HOST); - $this->_cfg_port = $this->conf(Base::O_OBJECT_PORT); - $this->_cfg_life = $this->conf(Base::O_OBJECT_LIFE); - $this->_cfg_persistent = $this->conf(Base::O_OBJECT_PERSISTENT); - $this->_cfg_admin = $this->conf(Base::O_OBJECT_ADMIN); - $this->_cfg_transients = $this->conf(Base::O_OBJECT_TRANSIENTS); - $this->_cfg_db = $this->conf(Base::O_OBJECT_DB_ID); - $this->_cfg_user = $this->conf(Base::O_OBJECT_USER); - $this->_cfg_pswd = $this->conf(Base::O_OBJECT_PSWD); - $this->_global_groups = $this->conf(Base::O_OBJECT_GLOBAL_GROUPS); - $this->_non_persistent_groups = $this->conf(Base::O_OBJECT_NON_PERSISTENT_GROUPS); - - if ($this->_cfg_method) { + $this->_cfg_enabled = $cfg[ Base::O_OBJECT ] && class_exists( $this->_oc_driver ) && $this->_cfg_host; + } elseif ( defined( 'LITESPEED_CONF_LOADED' ) ) { // If OC is OFF, will hit here to init OC after conf initialized + $this->_cfg_debug = $this->conf( Base::O_DEBUG ) ? $this->conf( Base::O_DEBUG ) : false; + $this->_cfg_method = $this->conf( Base::O_OBJECT_KIND ) ? true : false; + $this->_cfg_host = $this->conf( Base::O_OBJECT_HOST ); + $this->_cfg_port = $this->conf( Base::O_OBJECT_PORT ); + $this->_cfg_life = $this->conf( Base::O_OBJECT_LIFE ); + $this->_cfg_persistent = $this->conf( Base::O_OBJECT_PERSISTENT ); + $this->_cfg_admin = $this->conf( Base::O_OBJECT_ADMIN ); + $this->_cfg_transients = $this->conf( Base::O_OBJECT_TRANSIENTS ); + $this->_cfg_db = $this->conf( Base::O_OBJECT_DB_ID ); + $this->_cfg_user = $this->conf( Base::O_OBJECT_USER ); + $this->_cfg_pswd = $this->conf( Base::O_OBJECT_PSWD ); + $this->_global_groups = $this->conf( Base::O_OBJECT_GLOBAL_GROUPS ); + $this->_non_persistent_groups = $this->conf( Base::O_OBJECT_NON_PERSISTENT_GROUPS ); + + if ( $this->_cfg_method ) { $this->_oc_driver = 'Redis'; } - $this->_cfg_enabled = $this->conf(Base::O_OBJECT) && class_exists($this->_oc_driver) && $this->_cfg_host; - } elseif (defined('self::CONF_FILE') && file_exists(WP_CONTENT_DIR . '/' . self::CONF_FILE)) { - // Get cfg from _data_file - // Use self::const to avoid loading more classes - $cfg = \json_decode(file_get_contents(WP_CONTENT_DIR . '/' . self::CONF_FILE), true); - if (!empty($cfg[self::O_OBJECT_HOST])) { - $this->_cfg_debug = !empty($cfg[Base::O_DEBUG]) ? $cfg[Base::O_DEBUG] : false; - $this->_cfg_method = !empty($cfg[self::O_OBJECT_KIND]) ? $cfg[self::O_OBJECT_KIND] : false; - $this->_cfg_host = $cfg[self::O_OBJECT_HOST]; - $this->_cfg_port = $cfg[self::O_OBJECT_PORT]; - $this->_cfg_life = !empty($cfg[self::O_OBJECT_LIFE]) ? $cfg[self::O_OBJECT_LIFE] : $this->_default_life; - $this->_cfg_persistent = !empty($cfg[self::O_OBJECT_PERSISTENT]) ? $cfg[self::O_OBJECT_PERSISTENT] : false; - $this->_cfg_admin = !empty($cfg[self::O_OBJECT_ADMIN]) ? $cfg[self::O_OBJECT_ADMIN] : false; - $this->_cfg_transients = !empty($cfg[self::O_OBJECT_TRANSIENTS]) ? $cfg[self::O_OBJECT_TRANSIENTS] : false; - $this->_cfg_db = !empty($cfg[self::O_OBJECT_DB_ID]) ? $cfg[self::O_OBJECT_DB_ID] : 0; - $this->_cfg_user = !empty($cfg[self::O_OBJECT_USER]) ? $cfg[self::O_OBJECT_USER] : ''; - $this->_cfg_pswd = !empty($cfg[self::O_OBJECT_PSWD]) ? $cfg[self::O_OBJECT_PSWD] : ''; - $this->_global_groups = !empty($cfg[self::O_OBJECT_GLOBAL_GROUPS]) ? $cfg[self::O_OBJECT_GLOBAL_GROUPS] : array(); - $this->_non_persistent_groups = !empty($cfg[self::O_OBJECT_NON_PERSISTENT_GROUPS]) ? $cfg[self::O_OBJECT_NON_PERSISTENT_GROUPS] : array(); - - if ($this->_cfg_method) { + $this->_cfg_enabled = $this->conf( Base::O_OBJECT ) && class_exists( $this->_oc_driver ) && $this->_cfg_host; + } elseif ( defined( 'self::CONF_FILE' ) && file_exists( WP_CONTENT_DIR . '/' . self::CONF_FILE ) ) { + // Get cfg from _data_file. + // Use self::const to avoid loading more classes. + $cfg = \json_decode( file_get_contents( WP_CONTENT_DIR . '/' . self::CONF_FILE ), true ); + if ( ! empty( $cfg[ self::O_OBJECT_HOST ] ) ) { + $this->_cfg_debug = ! empty( $cfg[ Base::O_DEBUG ] ) ? $cfg[ Base::O_DEBUG ] : false; + $this->_cfg_method = ! empty( $cfg[ self::O_OBJECT_KIND ] ) ? $cfg[ self::O_OBJECT_KIND ] : false; + $this->_cfg_host = $cfg[ self::O_OBJECT_HOST ]; + $this->_cfg_port = $cfg[ self::O_OBJECT_PORT ]; + $this->_cfg_life = ! empty( $cfg[ self::O_OBJECT_LIFE ] ) ? $cfg[ self::O_OBJECT_LIFE ] : $this->_default_life; + $this->_cfg_persistent = ! empty( $cfg[ self::O_OBJECT_PERSISTENT ] ) ? $cfg[ self::O_OBJECT_PERSISTENT ] : false; + $this->_cfg_admin = ! empty( $cfg[ self::O_OBJECT_ADMIN ] ) ? $cfg[ self::O_OBJECT_ADMIN ] : false; + $this->_cfg_transients = ! empty( $cfg[ self::O_OBJECT_TRANSIENTS ] ) ? $cfg[ self::O_OBJECT_TRANSIENTS ] : false; + $this->_cfg_db = ! empty( $cfg[ self::O_OBJECT_DB_ID ] ) ? $cfg[ self::O_OBJECT_DB_ID ] : 0; + $this->_cfg_user = ! empty( $cfg[ self::O_OBJECT_USER ] ) ? $cfg[ self::O_OBJECT_USER ] : ''; + $this->_cfg_pswd = ! empty( $cfg[ self::O_OBJECT_PSWD ] ) ? $cfg[ self::O_OBJECT_PSWD ] : ''; + $this->_global_groups = ! empty( $cfg[ self::O_OBJECT_GLOBAL_GROUPS ] ) ? $cfg[ self::O_OBJECT_GLOBAL_GROUPS ] : array(); + $this->_non_persistent_groups = ! empty( $cfg[ self::O_OBJECT_NON_PERSISTENT_GROUPS ] ) ? $cfg[ self::O_OBJECT_NON_PERSISTENT_GROUPS ] : array(); + + if ( $this->_cfg_method ) { $this->_oc_driver = 'Redis'; } - $this->_cfg_enabled = class_exists($this->_oc_driver) && $this->_cfg_host; + $this->_cfg_enabled = class_exists( $this->_oc_driver ) && $this->_cfg_host; } else { $this->_cfg_enabled = false; } @@ -142,270 +329,302 @@ public function __construct( $cfg = false ) { * * @since 6.3 * @access private + * + * @param string $text Log text. + * @return void */ - private function debug_oc( $text, $show_error = false ) { - if (defined('LSCWP_LOG')) { - Debug2::debug($text); - + private function debug_oc( $text ) { + if ( defined( 'LSCWP_LOG' ) ) { + self::debug( $text ); return; } - if (!$show_error && $this->_cfg_debug != BASE::VAL_ON2) { + if ( Base::VAL_ON2 !== $this->_cfg_debug ) { return; } - $LITESPEED_DATA_FOLDER = defined('LITESPEED_DATA_FOLDER') ? LITESPEED_DATA_FOLDER : 'litespeed'; - $LSCWP_CONTENT_DIR = defined('LSCWP_CONTENT_DIR') ? LSCWP_CONTENT_DIR : WP_CONTENT_DIR; - $LITESPEED_STATIC_DIR = $LSCWP_CONTENT_DIR . '/' . $LITESPEED_DATA_FOLDER; - $log_path_prefix = $LITESPEED_STATIC_DIR . '/debug/'; - $log_file = $log_path_prefix . Debug2::FilePath('debug'); + $litespeed_data_folder = defined( 'LITESPEED_DATA_FOLDER' ) ? LITESPEED_DATA_FOLDER : 'litespeed'; + $lscwp_content_dir = defined( 'LSCWP_CONTENT_DIR' ) ? LSCWP_CONTENT_DIR : WP_CONTENT_DIR; + $litespeed_static_dir = $lscwp_content_dir . '/' . $litespeed_data_folder; + $log_path_prefix = $litespeed_static_dir . '/debug/'; + $log_file = $log_path_prefix . Debug2::FilePath( 'debug' ); - if (file_exists($log_path_prefix . 'index.php') && file_exists($log_file)) { + if ( file_exists( $log_path_prefix . 'index.php' ) && file_exists( $log_file ) ) { + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log(gmdate('m/d/y H:i:s') . ' - OC - ' . $text . PHP_EOL, 3, $log_file); } } /** - * Get `Store Transients` setting value + * Get `Store Transients` setting value. * * @since 1.8.3 * @access public + * + * @param string $group Group name. + * @return bool */ public function store_transients( $group ) { - return $this->_cfg_transients && $this->_is_transients_group($group); + return $this->_cfg_transients && $this->_is_transients_group( $group ); } /** - * Check if the group belongs to transients or not + * Check if the group belongs to transients or not. * * @since 1.8.3 * @access private + * + * @param string $group Group name. + * @return bool */ private function _is_transients_group( $group ) { - return in_array($group, array( 'transient', 'site-transient' )); + return in_array( $group, array( 'transient', 'site-transient' ), true ); } /** - * Update WP object cache file config + * Update WP object cache file config. * * @since 1.8 * @access public + * + * @param array $options Options to apply after update. + * @return void */ public function update_file( $options ) { $changed = false; - // NOTE: When included in oc.php, `LSCWP_DIR` will show undefined, so this must be assigned/generated when used + // NOTE: When included in oc.php, `LSCWP_DIR` will show undefined, so this must be assigned/generated when used. $_oc_ori_file = LSCWP_DIR . 'lib/object-cache.php'; $_oc_wp_file = WP_CONTENT_DIR . '/object-cache.php'; - // Update cls file - if (!file_exists($_oc_wp_file) || md5_file($_oc_wp_file) !== md5_file($_oc_ori_file)) { - $this->debug_oc('copying object-cache.php file to ' . $_oc_wp_file); - copy($_oc_ori_file, $_oc_wp_file); - + // Update cls file. + if ( ! file_exists( $_oc_wp_file ) || md5_file( $_oc_wp_file ) !== md5_file( $_oc_ori_file ) ) { + $this->debug_oc( 'copying object-cache.php file to ' . $_oc_wp_file ); + copy( $_oc_ori_file, $_oc_wp_file ); $changed = true; } /** - * Clear object cache + * Clear object cache. */ - if ($changed) { - $this->_reconnect($options); + if ( $changed ) { + $this->_reconnect( $options ); } } /** - * Remove object cache file + * Remove object cache file. * * @since 1.8.2 * @access public + * + * @return void */ public function del_file() { - // NOTE: When included in oc.php, `LSCWP_DIR` will show undefined, so this must be assigned/generated when used + // NOTE: When included in oc.php, `LSCWP_DIR` will show undefined, so this must be assigned/generated when used. $_oc_ori_file = LSCWP_DIR . 'lib/object-cache.php'; $_oc_wp_file = WP_CONTENT_DIR . '/object-cache.php'; - if (file_exists($_oc_wp_file) && md5_file($_oc_wp_file) === md5_file($_oc_ori_file)) { - $this->debug_oc('removing ' . $_oc_wp_file); - unlink($_oc_wp_file); + if ( file_exists( $_oc_wp_file ) && md5_file( $_oc_wp_file ) === md5_file( $_oc_ori_file ) ) { + $this->debug_oc( 'removing ' . $_oc_wp_file ); + wp_delete_file( $_oc_wp_file ); } } /** - * Try to build connection + * Try to build connection. * * @since 1.8 * @access public + * + * @return bool|null False on failure, true on success, null if unsupported. */ public function test_connection() { return $this->_connect(); } /** - * Force to connect with this setting + * Force to connect with this setting. * * @since 1.8 * @access private + * + * @param array $cfg Reconnect configuration. + * @return void */ private function _reconnect( $cfg ) { - $this->debug_oc('Reconnecting'); - if (isset($this->_conn)) { + $this->debug_oc( 'Reconnecting' ); + if ( isset( $this->_conn ) ) { // error_log( 'Object: Quitting existing connection!' ); - $this->debug_oc('Quitting existing connection'); + $this->debug_oc( 'Quitting existing connection' ); $this->flush(); $this->_conn = null; - $this->cls(false, true); + $this->cls( false, true ); } - $cls = $this->cls(false, false, $cfg); + $cls = $this->cls( false, false, $cfg ); $cls->_connect(); - if (isset($cls->_conn)) { + if ( isset( $cls->_conn ) ) { $cls->flush(); } } /** - * Connect to Memcached/Redis server + * Connect to Memcached/Redis server. * * @since 1.8 * @access private + * + * @return bool|null False on failure, true on success, null if driver missing. */ private function _connect() { - if (isset($this->_conn)) { + if ( isset( $this->_conn ) ) { // error_log( 'Object: _connected' ); return true; } - if (!class_exists($this->_oc_driver) || !$this->_cfg_host) { + if ( ! class_exists( $this->_oc_driver ) || ! $this->_cfg_host ) { + $this->debug_oc( '_oc_driver cls non existed or _cfg_host missed: ' . $this->_oc_driver . ' [_cfg_host] ' . $this->_cfg_host . ':' . $this->_cfg_port ); return null; } - if (defined('LITESPEED_OC_FAILURE')) { + if ( defined( 'LITESPEED_OC_FAILURE' ) ) { + $this->debug_oc( 'LITESPEED_OC_FAILURE const defined' ); return false; } - $this->debug_oc('Init ' . $this->_oc_driver . ' connection to ' . $this->_cfg_host . ':' . $this->_cfg_port); + $this->debug_oc( 'Init ' . $this->_oc_driver . ' connection to ' . $this->_cfg_host . ':' . $this->_cfg_port ); $failed = false; + /** - * Connect to Redis + * Connect to Redis. * * @since 1.8.1 * @see https://github.com/phpredis/phpredis/#example-1 */ - if ($this->_oc_driver == 'Redis') { - set_error_handler('litespeed_exception_handler'); + if ( 'Redis' === $this->_oc_driver ) { + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler + set_error_handler( 'litespeed_exception_handler' ); try { $this->_conn = new \Redis(); // error_log( 'Object: _connect Redis' ); - if ($this->_cfg_persistent) { - if ($this->_cfg_port) { - $this->_conn->pconnect($this->_cfg_host, $this->_cfg_port); + if ( $this->_cfg_persistent ) { + if ( $this->_cfg_port ) { + $this->_conn->pconnect( $this->_cfg_host, $this->_cfg_port ); } else { - $this->_conn->pconnect($this->_cfg_host); + $this->_conn->pconnect( $this->_cfg_host ); } - } elseif ($this->_cfg_port) { - $this->_conn->connect($this->_cfg_host, $this->_cfg_port); + } elseif ( $this->_cfg_port ) { + $this->_conn->connect( $this->_cfg_host, $this->_cfg_port ); } else { - $this->_conn->connect($this->_cfg_host); + $this->_conn->connect( $this->_cfg_host ); } - if ($this->_cfg_pswd) { - if ($this->_cfg_user) { - $this->_conn->auth(array( $this->_cfg_user, $this->_cfg_pswd )); + if ( $this->_cfg_pswd ) { + if ( $this->_cfg_user ) { + $this->_conn->auth( array( $this->_cfg_user, $this->_cfg_pswd ) ); } else { - $this->_conn->auth($this->_cfg_pswd); + $this->_conn->auth( $this->_cfg_pswd ); } } - if ($this->_cfg_db) { - $this->_conn->select($this->_cfg_db); + if (defined('Redis::OPT_REPLY_LITERAL')) { + $this->debug_oc( 'Redis set OPT_REPLY_LITERAL' ); + $this->_conn->setOption(\Redis::OPT_REPLY_LITERAL, true); } - $res = $this->_conn->ping(); + if ( $this->_cfg_db ) { + $this->_conn->select( $this->_cfg_db ); + } - if ($res != '+PONG') { + $res = $this->_conn->rawCommand('PING'); + + if ( 'PONG' !== $res ) { + $this->debug_oc( 'Redis resp is wrong: ' . $res ); $failed = true; } - } catch (\Exception $e) { - $this->debug_oc('Redis connect exception: ' . $e->getMessage(), true); + } catch ( \Exception $e ) { + $this->debug_oc( 'Redis connect exception: ' . $e->getMessage() ); $failed = true; - } catch (\ErrorException $e) { - $this->debug_oc('Redis connect error: ' . $e->getMessage(), true); + } catch ( \ErrorException $e ) { + $this->debug_oc( 'Redis connect error: ' . $e->getMessage() ); $failed = true; } restore_error_handler(); } else { - // Connect to Memcached - if ($this->_cfg_persistent) { - $this->_conn = new \Memcached($this->_get_mem_id()); + // Connect to Memcached. + if ( $this->_cfg_persistent ) { + $this->_conn = new \Memcached( $this->_get_mem_id() ); - // Check memcached persistent connection - if ($this->_validate_mem_server()) { + // Check memcached persistent connection. + if ( $this->_validate_mem_server() ) { // error_log( 'Object: _validate_mem_server' ); - $this->debug_oc('Got persistent ' . $this->_oc_driver . ' connection'); + $this->debug_oc( 'Got persistent ' . $this->_oc_driver . ' connection' ); return true; } - $this->debug_oc('No persistent ' . $this->_oc_driver . ' server list!'); + $this->debug_oc( 'No persistent ' . $this->_oc_driver . ' server list!' ); } else { // error_log( 'Object: new memcached!' ); $this->_conn = new \Memcached(); } - $this->_conn->addServer($this->_cfg_host, (int) $this->_cfg_port); + $this->_conn->addServer( $this->_cfg_host, (int) $this->_cfg_port ); /** - * Add SASL auth + * Add SASL auth. * * @since 1.8.1 * @since 2.9.6 Fixed SASL connection @see https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:lsmcd:new_sasl */ - if ($this->_cfg_user && $this->_cfg_pswd && method_exists($this->_conn, 'setSaslAuthData')) { - $this->_conn->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); - $this->_conn->setOption(\Memcached::OPT_COMPRESSION, false); - $this->_conn->setSaslAuthData($this->_cfg_user, $this->_cfg_pswd); + if ( $this->_cfg_user && $this->_cfg_pswd && method_exists( $this->_conn, 'setSaslAuthData' ) ) { + $this->_conn->setOption( \Memcached::OPT_BINARY_PROTOCOL, true ); + $this->_conn->setOption( \Memcached::OPT_COMPRESSION, false ); + $this->_conn->setSaslAuthData( $this->_cfg_user, $this->_cfg_pswd ); } - // Check connection - if (!$this->_validate_mem_server()) { + // Check connection. + if ( ! $this->_validate_mem_server() ) { $failed = true; } } - // If failed to connect - if ($failed) { - $this->debug_oc('❌ Failed to connect ' . $this->_oc_driver . ' server!', true); + // If failed to connect. + if ( $failed ) { + $this->debug_oc( '❌ Failed to connect ' . $this->_oc_driver . ' server!' ); $this->_conn = null; $this->_cfg_enabled = false; - !defined('LITESPEED_OC_FAILURE') && define('LITESPEED_OC_FAILURE', true); + ! defined( 'LITESPEED_OC_FAILURE' ) && define( 'LITESPEED_OC_FAILURE', true ); // error_log( 'Object: false!' ); return false; } - $this->debug_oc('Connected'); + $this->debug_oc( '✅ Connected to ' . $this->_oc_driver . ' server.' ); return true; } /** - * Check if the connected memcached host is the one in cfg + * Check if the connected memcached host is the one in cfg. * * @since 1.8 * @access private + * + * @return bool */ private function _validate_mem_server() { $mem_list = $this->_conn->getStats(); - if (empty($mem_list)) { + if ( empty( $mem_list ) ) { return false; } - foreach ($mem_list as $k => $v) { - if (substr($k, 0, strlen($this->_cfg_host)) != $this->_cfg_host) { + foreach ( $mem_list as $k => $v ) { + if ( substr( $k, 0, strlen( $this->_cfg_host ) ) !== $this->_cfg_host ) { continue; } - if (!empty($v['pid']) || !empty($v['curr_connections'])) { + if ( ! empty( $v['pid'] ) || ! empty( $v['curr_connections'] ) ) { return true; } } @@ -414,14 +633,16 @@ private function _validate_mem_server() { } /** - * Get memcached unique id to be used for connecting + * Get memcached unique id to be used for connecting. * * @since 1.8 * @access private + * + * @return string */ private function _get_mem_id() { $mem_id = 'litespeed'; - if (is_multisite()) { + if ( is_multisite() ) { $mem_id .= '_' . get_current_blog_id(); } @@ -429,37 +650,45 @@ private function _get_mem_id() { } /** - * Get cache + * Get cache. * * @since 1.8 * @access public + * + * @param string $key Cache key. + * @return mixed|null */ public function get( $key ) { - if (!$this->_cfg_enabled) { + if ( ! $this->_cfg_enabled ) { return null; } - if (!$this->_can_cache()) { + if ( ! $this->_can_cache() ) { return null; } - if (!$this->_connect()) { + if ( ! $this->_connect() ) { return null; } - $res = $this->_conn->get($key); + $res = $this->_conn->get( $key ); return $res; } /** - * Set cache + * Set cache. * * @since 1.8 * @access public + * + * @param string $key Cache key. + * @param mixed $data Data to store. + * @param int $expire TTL seconds. + * @return bool|null */ public function set( $key, $data, $expire ) { - if (!$this->_cfg_enabled) { + if ( ! $this->_cfg_enabled ) { return null; } @@ -471,83 +700,91 @@ public function set( $key, $data, $expire ) { // return null; // } - if (!$this->_connect()) { + if ( ! $this->_connect() ) { return null; } - $ttl = $expire ?: $this->_cfg_life; - if ($this->_oc_driver == 'Redis') { + $ttl = $expire ? $expire : $this->_cfg_life; + + if ( 'Redis' === $this->_oc_driver ) { try { - $res = $this->_conn->setEx($key, $ttl, $data); - } catch (\RedisException $ex) { + $res = $this->_conn->setEx( $key, $ttl, $data ); + } catch ( \RedisException $ex ) { $res = false; - $msg = sprintf(__('Redis encountered a fatal error: %1$s (code: %2$d)', 'litespeed-cache'), $ex->getMessage(), $ex->getCode()); - $this->debug_oc($msg); - Admin_Display::error($msg); + $msg = sprintf( __( 'Redis encountered a fatal error: %1$s (code: %2$d)', 'litespeed-cache' ), $ex->getMessage(), $ex->getCode() ); + $this->debug_oc( $msg ); + Admin_Display::error( $msg ); } } else { - $res = $this->_conn->set($key, $data, $ttl); + $res = $this->_conn->set( $key, $data, $ttl ); } return $res; } /** - * Check if can cache or not + * Check if can cache or not. * * @since 1.8 * @access private + * + * @return bool */ private function _can_cache() { - if (!$this->_cfg_admin && defined('WP_ADMIN')) { + if ( ! $this->_cfg_admin && defined( 'WP_ADMIN' ) ) { return false; } return true; } /** - * Delete cache + * Delete cache. * * @since 1.8 * @access public + * + * @param string $key Cache key. + * @return bool|null */ public function delete( $key ) { - if (!$this->_cfg_enabled) { + if ( ! $this->_cfg_enabled ) { return null; } - if (!$this->_connect()) { + if ( ! $this->_connect() ) { return null; } - if ($this->_oc_driver == 'Redis') { - $res = $this->_conn->del($key); + if ( 'Redis' === $this->_oc_driver ) { + $res = $this->_conn->del( $key ); } else { - $res = $this->_conn->delete($key); + $res = $this->_conn->delete( $key ); } return (bool) $res; } /** - * Clear all cache + * Clear all cache. * * @since 1.8 * @access public + * + * @return bool|null */ public function flush() { - if (!$this->_cfg_enabled) { - $this->debug_oc('bypass flushing'); + if ( ! $this->_cfg_enabled ) { + $this->debug_oc( 'bypass flushing' ); return null; } - if (!$this->_connect()) { + if ( ! $this->_connect() ) { return null; } - $this->debug_oc('flush!'); + $this->debug_oc( 'flush!' ); - if ($this->_oc_driver == 'Redis') { + if ( 'Redis' === $this->_oc_driver ) { $res = $this->_conn->flushDb(); } else { $res = $this->_conn->flush(); @@ -558,52 +795,64 @@ public function flush() { } /** - * Add global groups + * Add global groups. * * @since 1.8 * @access public + * + * @param string|string[] $groups Group(s) to add. + * @return void */ public function add_global_groups( $groups ) { - if (!is_array($groups)) { + if ( ! is_array( $groups ) ) { $groups = array( $groups ); } - $this->_global_groups = array_merge($this->_global_groups, $groups); - $this->_global_groups = array_unique($this->_global_groups); + $this->_global_groups = array_merge( $this->_global_groups, $groups ); + $this->_global_groups = array_unique( $this->_global_groups ); } /** - * Check if is in global groups or not + * Check if is in global groups or not. * * @since 1.8 * @access public + * + * @param string $group Group name. + * @return bool */ public function is_global( $group ) { - return in_array($group, $this->_global_groups); + return in_array( $group, $this->_global_groups, true ); } /** - * Add non persistent groups + * Add non persistent groups. * * @since 1.8 * @access public + * + * @param string|string[] $groups Group(s) to add. + * @return void */ public function add_non_persistent_groups( $groups ) { - if (!is_array($groups)) { + if ( ! is_array( $groups ) ) { $groups = array( $groups ); } - $this->_non_persistent_groups = array_merge($this->_non_persistent_groups, $groups); - $this->_non_persistent_groups = array_unique($this->_non_persistent_groups); + $this->_non_persistent_groups = array_merge( $this->_non_persistent_groups, $groups ); + $this->_non_persistent_groups = array_unique( $this->_non_persistent_groups ); } /** - * Check if is in non persistent groups or not + * Check if is in non persistent groups or not. * * @since 1.8 * @access public + * + * @param string $group Group name. + * @return bool */ public function is_non_persistent( $group ) { - return in_array($group, $this->_non_persistent_groups); + return in_array( $group, $this->_non_persistent_groups, true ); } } diff --git a/src/object.lib.php b/src/object.lib.php index c2b2959d2..4c58bd8d6 100644 --- a/src/object.lib.php +++ b/src/object.lib.php @@ -3,28 +3,44 @@ * LiteSpeed Object Cache Library * * @since 1.8 + * @package LiteSpeed */ -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); + +if (!function_exists('litespeed_exception_handler')) { /** * Handle exception + * + * Converts PHP errors into exceptions for better error handling. + * + * @since 1.8 + * @access public + * @param int $errno Error level. + * @param string $errstr Error message. + * @param string $errfile File where the error occurred. + * @param int $errline Line number where the error occurred. + * @throws \ErrorException Error msg. */ -if (!function_exists('litespeed_exception_handler')) { - function litespeed_exception_handler( $errno, $errstr, $errfile, $errline ) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); - } +function litespeed_exception_handler( $errno, $errstr, $errfile, $errline ) { + throw new \ErrorException( esc_html( $errstr ), 0, esc_html( $errno ), esc_html( $errfile ), esc_html( $errline ) ); +} } require_once __DIR__ . '/object-cache.cls.php'; +require_once __DIR__ . '/object-cache-wp.cls.php'; /** * Sets up Object Cache Global and assigns it. * - * @since 1.8 + * Initializes the global object cache instance. * - * @global WP_Object_Cache $wp_object_cache + * @since 1.8 + * @access public + * @global WP_Object_Cache $wp_object_cache Object cache global instance. */ function wp_cache_init() { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $GLOBALS['wp_object_cache'] = WP_Object_Cache::get_instance(); } @@ -32,7 +48,7 @@ function wp_cache_init() { * Adds data to the cache, if the cache key doesn't already exist. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::add() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -47,14 +63,14 @@ function wp_cache_init() { function wp_cache_add( $key, $data, $group = '', $expire = 0 ) { global $wp_object_cache; - return $wp_object_cache->add($key, $data, $group, (int) $expire); + return $wp_object_cache->add( $key, $data, $group, (int) $expire ); } /** * Adds multiple values to the cache in one call. * * @since 5.4 - * + * @access public * @see WP_Object_Cache::add_multiple() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -68,14 +84,14 @@ function wp_cache_add( $key, $data, $group = '', $expire = 0 ) { function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) { global $wp_object_cache; - return $wp_object_cache->add_multiple($data, $group, $expire); + return $wp_object_cache->add_multiple( $data, $group, $expire ); } /** * Replaces the contents of the cache with new data. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::replace() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -90,7 +106,7 @@ function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) { function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { global $wp_object_cache; - return $wp_object_cache->replace($key, $data, $group, (int) $expire); + return $wp_object_cache->replace( $key, $data, $group, (int) $expire ); } /** @@ -99,7 +115,7 @@ function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { * Differs from wp_cache_add() and wp_cache_replace() in that it will always write data. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::set() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -114,14 +130,14 @@ function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { global $wp_object_cache; - return $wp_object_cache->set($key, $data, $group, (int) $expire); + return $wp_object_cache->set( $key, $data, $group, (int) $expire ); } /** * Sets multiple values to the cache in one call. * * @since 5.4 - * + * @access public * @see WP_Object_Cache::set_multiple() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -135,14 +151,14 @@ function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) { global $wp_object_cache; - return $wp_object_cache->set_multiple($data, $group, $expire); + return $wp_object_cache->set_multiple( $data, $group, $expire ); } /** * Retrieves the cache contents from the cache by key and group. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::get() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -157,14 +173,14 @@ function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) { function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { global $wp_object_cache; - return $wp_object_cache->get($key, $group, $force, $found); + return $wp_object_cache->get( $key, $group, $force, $found ); } /** * Retrieves multiple values from the cache in one call. * * @since 5.4 - * + * @access public * @see WP_Object_Cache::get_multiple() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -178,14 +194,14 @@ function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { function wp_cache_get_multiple( $keys, $group = '', $force = false ) { global $wp_object_cache; - return $wp_object_cache->get_multiple($keys, $group, $force); + return $wp_object_cache->get_multiple( $keys, $group, $force ); } /** * Removes the cache contents matching key and group. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::delete() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -196,14 +212,14 @@ function wp_cache_get_multiple( $keys, $group = '', $force = false ) { function wp_cache_delete( $key, $group = '' ) { global $wp_object_cache; - return $wp_object_cache->delete($key, $group); + return $wp_object_cache->delete( $key, $group ); } /** * Deletes multiple values from the cache in one call. * * @since 5.4 - * + * @access public * @see WP_Object_Cache::delete_multiple() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -215,14 +231,14 @@ function wp_cache_delete( $key, $group = '' ) { function wp_cache_delete_multiple( array $keys, $group = '' ) { global $wp_object_cache; - return $wp_object_cache->delete_multiple($keys, $group); + return $wp_object_cache->delete_multiple( $keys, $group ); } /** * Increments numeric cache item's value. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::incr() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -235,14 +251,14 @@ function wp_cache_delete_multiple( array $keys, $group = '' ) { function wp_cache_incr( $key, $offset = 1, $group = '' ) { global $wp_object_cache; - return $wp_object_cache->incr($key, $offset, $group); + return $wp_object_cache->incr( $key, $offset, $group ); } /** * Decrements numeric cache item's value. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::decr() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -255,14 +271,14 @@ function wp_cache_incr( $key, $offset = 1, $group = '' ) { function wp_cache_decr( $key, $offset = 1, $group = '' ) { global $wp_object_cache; - return $wp_object_cache->decr($key, $offset, $group); + return $wp_object_cache->decr( $key, $offset, $group ); } /** * Removes all cache items. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::flush() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -278,7 +294,7 @@ function wp_cache_flush() { * Removes all cache items from the in-memory runtime cache. * * @since 5.4 - * + * @access public * @see WP_Object_Cache::flush_runtime() * * @return bool True on success, false on failure. @@ -296,7 +312,7 @@ function wp_cache_flush_runtime() { * `wp_cache_supports( 'flush_group' )` function. * * @since 5.4 - * + * @access public * @see WP_Object_Cache::flush_group() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -306,13 +322,14 @@ function wp_cache_flush_runtime() { function wp_cache_flush_group( $group ) { global $wp_object_cache; - return $wp_object_cache->flush_group($group); + return $wp_object_cache->flush_group( $group ); } /** * Determines whether the object cache implementation supports a particular feature. * * @since 5.4 + * @access public * * @param string $feature Name of the feature to check for. Possible values include: * 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple', @@ -320,7 +337,7 @@ function wp_cache_flush_group( $group ) { * @return bool True if the feature is supported, false otherwise. */ function wp_cache_supports( $feature ) { - switch ($feature) { + switch ( $feature ) { case 'add_multiple': case 'set_multiple': case 'get_multiple': @@ -344,6 +361,7 @@ function wp_cache_supports( $feature ) { * to make sure that the cache is cleaned up after WordPress no longer needs it. * * @since 1.8 + * @access public * * @return true Always returns true. */ @@ -355,7 +373,7 @@ function wp_cache_close() { * Adds a group or set of groups to the list of global groups. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::add_global_groups() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -364,20 +382,21 @@ function wp_cache_close() { function wp_cache_add_global_groups( $groups ) { global $wp_object_cache; - $wp_object_cache->add_global_groups($groups); + $wp_object_cache->add_global_groups( $groups ); } /** * Adds a group or set of groups to the list of non-persistent groups. * * @since 1.8 + * @access public * * @param string|string[] $groups A group or an array of groups to add. */ function wp_cache_add_non_persistent_groups( $groups ) { global $wp_object_cache; - $wp_object_cache->add_non_persistent_groups($groups); + $wp_object_cache->add_non_persistent_groups( $groups ); } /** @@ -386,7 +405,7 @@ function wp_cache_add_non_persistent_groups( $groups ) { * This changes the blog id used to create keys in blog specific groups. * * @since 1.8 - * + * @access public * @see WP_Object_Cache::switch_to_blog() * @global WP_Object_Cache $wp_object_cache Object cache global instance. * @@ -395,827 +414,5 @@ function wp_cache_add_non_persistent_groups( $groups ) { function wp_cache_switch_to_blog( $blog_id ) { global $wp_object_cache; - $wp_object_cache->switch_to_blog($blog_id); -} - -class WP_Object_Cache { - - protected static $_instance; - - private $_object_cache; - - private $_cache = array(); - private $_cache_404 = array(); - - private $cache_total = 0; - private $count_hit_incall = 0; - private $count_hit = 0; - private $count_miss_incall = 0; - private $count_miss = 0; - private $count_set = 0; - - protected $global_groups = array(); - private $blog_prefix; - private $multisite; - - /** - * Init. - * - * @since 1.8 - */ - public function __construct() { - $this->_object_cache = \LiteSpeed\Object_Cache::cls(); - - $this->multisite = is_multisite(); - $this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : ''; - - /** - * Fix multiple instance using same oc issue - * - * @since 1.8.2 - */ - !defined('LSOC_PREFIX') && define('LSOC_PREFIX', substr(md5(__FILE__), -5)); - } - - /** - * Makes private properties readable for backward compatibility. - * - * @since 5.4 - * @access public - * - * @param string $name Property to get. - * @return mixed Property. - */ - public function __get( $name ) { - return $this->$name; - } - - /** - * Makes private properties settable for backward compatibility. - * - * @since 5.4 - * @access public - * - * @param string $name Property to set. - * @param mixed $value Property value. - * @return mixed Newly-set property. - */ - public function __set( $name, $value ) { - return $this->$name = $value; - } - - /** - * Makes private properties checkable for backward compatibility. - * - * @since 5.4 - * @access public - * - * @param string $name Property to check if set. - * @return bool Whether the property is set. - */ - public function __isset( $name ) { - return isset($this->$name); - } - - /** - * Makes private properties un-settable for backward compatibility. - * - * @since 5.4 - * @access public - * - * @param string $name Property to unset. - */ - public function __unset( $name ) { - unset($this->$name); - } - - /** - * Serves as a utility function to determine whether a key is valid. - * - * @since 5.4 - * @access protected - * - * @param int|string $key Cache key to check for validity. - * @return bool Whether the key is valid. - */ - protected function is_valid_key( $key ) { - if (is_int($key)) { - return true; - } - - if (is_string($key) && trim($key) !== '') { - return true; - } - - $type = gettype($key); - - if (!function_exists('__')) { - wp_load_translations_early(); - } - - $message = is_string($key) - ? __('Cache key must not be an empty string.') - : /* translators: %s: The type of the given cache key. */ - sprintf(__('Cache key must be integer or non-empty string, %s given.'), $type); - - _doing_it_wrong(sprintf('%s::%s', __CLASS__, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']), $message, '6.1.0'); - - return false; - } - - /** - * Get the final key. - * - * @since 1.8 - * @access private - */ - private function _key( $key, $group = 'default' ) { - if (empty($group)) { - $group = 'default'; - } - - $prefix = $this->_object_cache->is_global($group) ? '' : $this->blog_prefix; - - return LSOC_PREFIX . $prefix . $group . '.' . $key; - } - - /** - * Output debug info. - * - * @since 1.8 - * @access public - */ - public function debug() { - return ' [total] ' . - $this->cache_total . - ' [hit_incall] ' . - $this->count_hit_incall . - ' [hit] ' . - $this->count_hit . - ' [miss_incall] ' . - $this->count_miss_incall . - ' [miss] ' . - $this->count_miss . - ' [set] ' . - $this->count_set; - } - - /** - * Adds data to the cache if it doesn't already exist. - * - * @since 1.8 - * @access public - * - * @uses WP_Object_Cache::_exists() Checks to see if the cache already has data. - * @uses WP_Object_Cache::set() Sets the data after the checking the cache - * contents existence. - * - * @param int|string $key What to call the contents in the cache. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Default 'default'. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True on success, false if cache key and group already exist. - */ - public function add( $key, $data, $group = 'default', $expire = 0 ) { - if (wp_suspend_cache_addition()) { - return false; - } - - if (!$this->is_valid_key($key)) { - return false; - } - - if (empty($group)) { - $group = 'default'; - } - - $id = $this->_key($key, $group); - - if (array_key_exists($id, $this->_cache)) { - return false; - } - - return $this->set($key, $data, $group, (int) $expire); - } - - /** - * Adds multiple values to the cache in one call. - * - * @since 5.4 - * @access public - * - * @param array $data Array of keys and values to be added. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false if cache key and group already exist. - */ - public function add_multiple( array $data, $group = '', $expire = 0 ) { - $values = array(); - - foreach ($data as $key => $value) { - $values[$key] = $this->add($key, $value, $group, $expire); - } - - return $values; - } - - /** - * Replaces the contents in the cache, if contents already exist. - * - * @since 1.8 - * @access public - * - * @see WP_Object_Cache::set() - * - * @param int|string $key What to call the contents in the cache. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Default 'default'. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True if contents were replaced, false if original value does not exist. - */ - public function replace( $key, $data, $group = 'default', $expire = 0 ) { - if (!$this->is_valid_key($key)) { - return false; - } - - if (empty($group)) { - $group = 'default'; - } - - $id = $this->_key($key, $group); - - if (!array_key_exists($id, $this->_cache)) { - return false; - } - - return $this->set($key, $data, $group, (int) $expire); - } - - /** - * Sets the data contents into the cache. - * - * The cache contents are grouped by the $group parameter followed by the - * $key. This allows for duplicate IDs in unique groups. Therefore, naming of - * the group should be used with care and should follow normal function - * naming guidelines outside of core WordPress usage. - * - * The $expire parameter is not used, because the cache will automatically - * expire for each time a page is accessed and PHP finishes. The method is - * more for cache plugins which use files. - * - * @since 1.8 - * @since 5.4 Returns false if cache key is invalid. - * @access public - * - * @param int|string $key What to call the contents in the cache. - * @param mixed $data The contents to store in the cache. - * @param string $group Optional. Where to group the cache contents. Default 'default'. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool True if contents were set, false if key is invalid. - */ - public function set( $key, $data, $group = 'default', $expire = 0 ) { - if (!$this->is_valid_key($key)) { - return false; - } - - if (empty($group)) { - $group = 'default'; - } - - $id = $this->_key($key, $group); - - if (is_object($data)) { - $data = clone $data; - } - // error_log("oc: set \t\t\t[key] " . $id ); - $this->_cache[$id] = $data; - - if (array_key_exists($id, $this->_cache_404)) { - // error_log("oc: unset404\t\t\t[key] " . $id ); - unset($this->_cache_404[$id]); - } - - if (!$this->_object_cache->is_non_persistent($group)) { - $this->_object_cache->set($id, serialize(array( 'data' => $data )), (int) $expire); - ++$this->count_set; - } - - if ($this->_object_cache->store_transients($group)) { - $this->_transient_set($key, $data, $group, (int) $expire); - } - - return true; - } - - /** - * Sets multiple values to the cache in one call. - * - * @since 5.4 - * @access public - * - * @param array $data Array of key and value to be set. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - * @return bool[] Array of return values, grouped by key. Each value is always true. - */ - public function set_multiple( array $data, $group = '', $expire = 0 ) { - $values = array(); - - foreach ($data as $key => $value) { - $values[$key] = $this->set($key, $value, $group, $expire); - } - - return $values; - } - - /** - * Retrieves the cache contents, if it exists. - * - * The contents will be first attempted to be retrieved by searching by the - * key in the cache group. If the cache is hit (success) then the contents - * are returned. - * - * On failure, the number of cache misses will be incremented. - * - * @since 1.8 - * @access public - * - * @param int|string $key The key under which the cache contents are stored. - * @param string $group Optional. Where the cache contents are grouped. Default 'default'. - * @param bool $force Optional. Unused. Whether to force an update of the local cache - * from the persistent cache. Default false. - * @param bool $found Optional. Whether the key was found in the cache (passed by reference). - * Disambiguates a return of false, a storable value. Default null. - * @return mixed|false The cache contents on success, false on failure to retrieve contents. - */ - public function get( $key, $group = 'default', $force = false, &$found = null ) { - if (!$this->is_valid_key($key)) { - return false; - } - - if (empty($group)) { - $group = 'default'; - } - - $id = $this->_key($key, $group); - - // error_log(''); - // error_log("oc: get \t\t\t[key] " . $id . ( $force ? "\t\t\t [forced] " : '' ) ); - $found = false; - $found_in_oc = false; - $cache_val = false; - if (array_key_exists($id, $this->_cache) && !$force) { - $found = true; - $cache_val = $this->_cache[$id]; - ++$this->count_hit_incall; - } elseif (!array_key_exists($id, $this->_cache_404) && !$this->_object_cache->is_non_persistent($group)) { - $v = $this->_object_cache->get($id); - - if ($v !== null) { - $v = @maybe_unserialize($v); - } - - // To be compatible with false val - if (is_array($v) && array_key_exists('data', $v)) { - ++$this->count_hit; - $found = true; - $found_in_oc = true; - $cache_val = $v['data']; - } else { - // Can't find key, cache it to 404 - // error_log("oc: add404\t\t\t[key] " . $id ); - $this->_cache_404[$id] = 1; - ++$this->count_miss; - } - } else { - ++$this->count_miss_incall; - } - - if (is_object($cache_val)) { - $cache_val = clone $cache_val; - } - - // If not found but has `Store Transients` cfg on, still need to follow WP's get_transient() logic - if (!$found && $this->_object_cache->store_transients($group)) { - $cache_val = $this->_transient_get($key, $group); - if ($cache_val) { - $found = true; // $found not used for now (v1.8.3) - } - } - - if ($found_in_oc) { - $this->_cache[$id] = $cache_val; - } - - ++$this->cache_total; - - return $cache_val; - } - - /** - * Retrieves multiple values from the cache in one call. - * - * @since 5.4 - * @access public - * - * @param array $keys Array of keys under which the cache contents are stored. - * @param string $group Optional. Where the cache contents are grouped. Default 'default'. - * @param bool $force Optional. Whether to force an update of the local cache - * from the persistent cache. Default false. - * @return array Array of return values, grouped by key. Each value is either - * the cache contents on success, or false on failure. - */ - public function get_multiple( $keys, $group = 'default', $force = false ) { - $values = array(); - - foreach ($keys as $key) { - $values[$key] = $this->get($key, $group, $force); - } - - return $values; - } - - /** - * Removes the contents of the cache key in the group. - * - * If the cache key does not exist in the group, then nothing will happen. - * - * @since 1.8 - * @access public - * - * @param int|string $key What the contents in the cache are called. - * @param string $group Optional. Where the cache contents are grouped. Default 'default'. - * @param bool $deprecated Optional. Unused. Default false. - * @return bool True on success, false if the contents were not deleted. - */ - public function delete( $key, $group = 'default', $deprecated = false ) { - if (!$this->is_valid_key($key)) { - return false; - } - - if (empty($group)) { - $group = 'default'; - } - - $id = $this->_key($key, $group); - - if ($this->_object_cache->store_transients($group)) { - $this->_transient_del($key, $group); - } - - if (array_key_exists($id, $this->_cache)) { - unset($this->_cache[$id]); - } - // error_log("oc: delete \t\t\t[key] " . $id ); - - if ($this->_object_cache->is_non_persistent($group)) { - return false; - } - - return $this->_object_cache->delete($id); - } - - /** - * Deletes multiple values from the cache in one call. - * - * @since 5.4 - * @access public - * - * @param array $keys Array of keys to be deleted. - * @param string $group Optional. Where the cache contents are grouped. Default empty. - * @return bool[] Array of return values, grouped by key. Each value is either - * true on success, or false if the contents were not deleted. - */ - public function delete_multiple( array $keys, $group = '' ) { - $values = array(); - - foreach ($keys as $key) { - $values[$key] = $this->delete($key, $group); - } - - return $values; - } - - /** - * Increments numeric cache item's value. - * - * @since 5.4 - * - * @param int|string $key The cache key to increment. - * @param int $offset Optional. The amount by which to increment the item's value. - * Default 1. - * @param string $group Optional. The group the key is in. Default 'default'. - * @return int|false The item's new value on success, false on failure. - */ - public function incr( $key, $offset = 1, $group = 'default' ) { - return $this->incr_desr($key, $offset, $group, true); - } - - /** - * Decrements numeric cache item's value. - * - * @since 5.4 - * - * @param int|string $key The cache key to decrement. - * @param int $offset Optional. The amount by which to decrement the item's value. - * Default 1. - * @param string $group Optional. The group the key is in. Default 'default'. - * @return int|false The item's new value on success, false on failure. - */ - public function decr( $key, $offset = 1, $group = 'default' ) { - return $this->incr_desr($key, $offset, $group, false); - } - - /** - * Increments or decrements numeric cache item's value. - * - * @since 1.8 - * @access public - */ - public function incr_desr( $key, $offset = 1, $group = 'default', $incr = true ) { - if (!$this->is_valid_key($key)) { - return false; - } - - if (empty($group)) { - $group = 'default'; - } - - $cache_val = $this->get($key, $group); - - if (false === $cache_val) { - return false; - } - - if (!is_numeric($cache_val)) { - $cache_val = 0; - } - - $offset = (int) $offset; - - if ($incr) { - $cache_val += $offset; - } else { - $cache_val -= $offset; - } - - if ($cache_val < 0) { - $cache_val = 0; - } - - $this->set($key, $cache_val, $group); - - return $cache_val; - } - - /** - * Clears the object cache of all data. - * - * @since 1.8 - * @access public - * - * @return true Always returns true. - */ - public function flush() { - $this->flush_runtime(); - - $this->_object_cache->flush(); - - return true; - } - - /** - * Removes all cache items from the in-memory runtime cache. - * - * @since 5.4 - * @access public - * - * @return true Always returns true. - */ - public function flush_runtime() { - $this->_cache = array(); - $this->_cache_404 = array(); - - return true; - } - - /** - * Removes all cache items in a group. - * - * @since 5.4 - * @access public - * - * @param string $group Name of group to remove from cache. - * @return true Always returns true. - */ - public function flush_group( $group ) { - // unset( $this->cache[ $group ] ); - - return true; - } - - /** - * Sets the list of global cache groups. - * - * @since 1.8 - * @access public - * - * @param string|string[] $groups List of groups that are global. - */ - public function add_global_groups( $groups ) { - $groups = (array) $groups; - - $this->_object_cache->add_global_groups($groups); - } - - /** - * Sets the list of non-persistent cache groups. - * - * @since 1.8 - * @access public - */ - public function add_non_persistent_groups( $groups ) { - $groups = (array) $groups; - - $this->_object_cache->add_non_persistent_groups($groups); - } - - /** - * Switches the internal blog ID. - * - * This changes the blog ID used to create keys in blog specific groups. - * - * @since 1.8 - * @access public - * - * @param int $blog_id Blog ID. - */ - public function switch_to_blog( $blog_id ) { - $blog_id = (int) $blog_id; - $this->blog_prefix = $this->multisite ? $blog_id . ':' : ''; - } - - /** - * Get transient from wp table - * - * @since 1.8.3 - * @access private - * @see `wp-includes/option.php` function `get_transient`/`set_site_transient` - */ - private function _transient_get( $transient, $group ) { - if ($group == 'transient') { - /**** Ori WP func start */ - $transient_option = '_transient_' . $transient; - if (!wp_installing()) { - // If option is not in alloptions, it is not autoloaded and thus has a timeout - $alloptions = wp_load_alloptions(); - if (!isset($alloptions[$transient_option])) { - $transient_timeout = '_transient_timeout_' . $transient; - $timeout = get_option($transient_timeout); - if (false !== $timeout && $timeout < time()) { - delete_option($transient_option); - delete_option($transient_timeout); - $value = false; - } - } - } - - if (!isset($value)) { - $value = get_option($transient_option); - } - /**** Ori WP func end */ - } elseif ($group == 'site-transient') { - /**** Ori WP func start */ - $no_timeout = array( 'update_core', 'update_plugins', 'update_themes' ); - $transient_option = '_site_transient_' . $transient; - if (!in_array($transient, $no_timeout)) { - $transient_timeout = '_site_transient_timeout_' . $transient; - $timeout = get_site_option($transient_timeout); - if (false !== $timeout && $timeout < time()) { - delete_site_option($transient_option); - delete_site_option($transient_timeout); - $value = false; - } - } - - if (!isset($value)) { - $value = get_site_option($transient_option); - } - /**** Ori WP func end */ - } else { - $value = false; - } - - return $value; - } - - /** - * Set transient to WP table - * - * @since 1.8.3 - * @access private - * @see `wp-includes/option.php` function `set_transient`/`set_site_transient` - */ - private function _transient_set( $transient, $value, $group, $expiration ) { - if ($group == 'transient') { - /**** Ori WP func start */ - $transient_timeout = '_transient_timeout_' . $transient; - $transient_option = '_transient_' . $transient; - if (false === get_option($transient_option)) { - $autoload = 'yes'; - if ((int) $expiration) { - $autoload = 'no'; - add_option($transient_timeout, time() + (int) $expiration, '', 'no'); - } - $result = add_option($transient_option, $value, '', $autoload); - } else { - // If expiration is requested, but the transient has no timeout option, - // delete, then re-create transient rather than update. - $update = true; - if ((int) $expiration) { - if (false === get_option($transient_timeout)) { - delete_option($transient_option); - add_option($transient_timeout, time() + (int) $expiration, '', 'no'); - $result = add_option($transient_option, $value, '', 'no'); - $update = false; - } else { - update_option($transient_timeout, time() + (int) $expiration); - } - } - if ($update) { - $result = update_option($transient_option, $value); - } - } - /**** Ori WP func end */ - } elseif ($group == 'site-transient') { - /**** Ori WP func start */ - $transient_timeout = '_site_transient_timeout_' . $transient; - $option = '_site_transient_' . $transient; - if (false === get_site_option($option)) { - if ((int) $expiration) { - add_site_option($transient_timeout, time() + (int) $expiration); - } - $result = add_site_option($option, $value); - } else { - if ((int) $expiration) { - update_site_option($transient_timeout, time() + (int) $expiration); - } - $result = update_site_option($option, $value); - } - /**** Ori WP func end */ - } else { - $result = null; - } - - return $result; - } - - /** - * Delete transient from WP table - * - * @since 1.8.3 - * @access private - * @see `wp-includes/option.php` function `delete_transient`/`delete_site_transient` - */ - private function _transient_del( $transient, $group ) { - if ($group == 'transient') { - /**** Ori WP func start */ - $option_timeout = '_transient_timeout_' . $transient; - $option = '_transient_' . $transient; - $result = delete_option($option); - if ($result) { - delete_option($option_timeout); - } - /**** Ori WP func end */ - } elseif ($group == 'site-transient') { - /**** Ori WP func start */ - $option_timeout = '_site_transient_timeout_' . $transient; - $option = '_site_transient_' . $transient; - $result = delete_site_option($option); - if ($result) { - delete_site_option($option_timeout); - } - /**** Ori WP func end */ - } - } - - /** - * Get the current instance object. - * - * @since 1.8 - * @access public - */ - public static function get_instance() { - if (!isset(self::$_instance)) { - self::$_instance = new self(); - } - - return self::$_instance; - } + $wp_object_cache->switch_to_blog( $blog_id ); } diff --git a/src/optimize.cls.php b/src/optimize.cls.php index c117e0fd8..3172120ba 100644 --- a/src/optimize.cls.php +++ b/src/optimize.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The optimize class. @@ -11,6 +12,7 @@ defined('WPINC') || exit(); class Optimize extends Base { + const LOG_TAG = '🎢'; const LIB_FILE_CSS_ASYNC = 'assets/js/css_async.min.js'; const LIB_FILE_WEBFONTLOADER = 'assets/js/webfontloader.min.js'; @@ -18,6 +20,8 @@ class Optimize extends Base { const ITEM_TIMESTAMP_PURGE_CSS = 'timestamp_purge_css'; + const DUMMY_CSS_REGEX = "#<link rel=['\"]stylesheet['\"] id=['\"]litespeed-cache-dummy-css['\"] href=['\"].+assets/css/litespeed-dummy\.css[?\w.=-]*['\"][ \w='\"/]*>#isU"; + private $content; private $content_ori; @@ -42,7 +46,8 @@ class Optimize extends Base { private $__optimizer; private $html_foot = ''; // The html info append to <body> - private $html_head = ''; // The html info prepend to <body> + private $html_head = ''; // The html info append to <head> + private $html_head_early = ''; // The html info prepend to top of head private static $_var_i = 0; private $_var_preserve_js = array(); @@ -54,7 +59,7 @@ class Optimize extends Base { * @since 4.0 */ public function __construct() { - Debug2::debug('[Optm] init'); + self::debug('init'); $this->__optimizer = $this->cls('Optimizer'); } @@ -68,11 +73,11 @@ public function init() { $this->cfg_css_async = defined('LITESPEED_GUEST_OPTM') || $this->conf(self::O_OPTM_CSS_ASYNC); if ($this->cfg_css_async) { if (!$this->cls('Cloud')->activated()) { - Debug2::debug('[Optm] ❌ CCSS set to OFF due to QC not activated'); + self::debug('❌ CCSS set to OFF due to QC not activated'); $this->cfg_css_async = false; } if ((defined('LITESPEED_GUEST_OPTM') || ($this->conf(self::O_OPTM_UCSS) && $this->conf(self::O_OPTM_CSS_COMB))) && $this->conf(self::O_OPTM_UCSS_INLINE)) { - Debug2::debug('[Optm] ⚠️ CCSS set to OFF due to UCSS Inline'); + self::debug('⚠️ CCSS set to OFF due to UCSS Inline'); $this->cfg_css_async = false; } } @@ -122,28 +127,16 @@ function ( $con, $file_type ) { } } - /** - * Add vary filter for Role Excludes - * - * @since 1.6 - */ + // Add vary filter for Role Excludes @since 1.6 add_filter('litespeed_vary', array( $this, 'vary_add_role_exclude' )); - /** - * Prefetch DNS - * - * @since 1.7.1 - */ - $this->_dns_prefetch_init(); - - /** - * Preconnect - * - * @since 5.6.1 - */ - $this->_dns_preconnect_init(); + // DNS optm (Prefetch/Preconnect) @since 7.3 + $this->_dns_optm_init(); add_filter('litespeed_buffer_finalize', array( $this, 'finalize' ), 20); + + // Inject a dummy CSS file to control final optimized data location in <head> + wp_enqueue_style(Core::PLUGIN_NAME . '-dummy', LSWCP_PLUGIN_URL . 'assets/css/litespeed-dummy.css'); } /** @@ -230,19 +223,28 @@ public function remove_query_strings( $src ) { * @return string The content that is after optimization */ public function finalize( $content ) { + $content = $this->_finalize($content); + // Fallback to replace dummy css placeholder + if (false !== preg_match(self::DUMMY_CSS_REGEX, $content)) { + self::debug('Fallback to drop dummy CSS'); + $content = preg_replace( self::DUMMY_CSS_REGEX, '', $content ); + } + return $content; + } + private function _finalize( $content ) { if (defined('LITESPEED_NO_PAGEOPTM')) { - Debug2::debug2('[Optm] bypass: NO_PAGEOPTM const'); + self::debug2('bypass: NO_PAGEOPTM const'); return $content; } if (!defined('LITESPEED_IS_HTML')) { - Debug2::debug('[Optm] bypass: Not frontend HTML type'); + self::debug('bypass: Not frontend HTML type'); return $content; } if (!defined('LITESPEED_GUEST_OPTM')) { if (!Control::is_cacheable()) { - Debug2::debug('[Optm] bypass: Not cacheable'); + self::debug('bypass: Not cacheable'); return $content; } @@ -251,12 +253,12 @@ public function finalize( $content ) { $excludes = apply_filters('litespeed_optm_uri_exc', $this->conf(self::O_OPTM_EXC)); $result = Utility::str_hit_array($_SERVER['REQUEST_URI'], $excludes); if ($result) { - Debug2::debug('[Optm] bypass: hit URI Excludes setting: ' . $result); + self::debug('bypass: hit URI Excludes setting: ' . $result); return $content; } } - Debug2::debug('[Optm] start'); + self::debug('start'); $this->content_ori = $this->content = $content; @@ -287,17 +289,17 @@ private function _optimize() { $this->_conf_css_font_display = !defined('LITESPEED_GUEST_OPTM') && $this->conf(self::O_OPTM_CSS_FONT_DISPLAY); if (!$this->cls('Router')->can_optm()) { - Debug2::debug('[Optm] bypass: admin/feed/preview'); + self::debug('bypass: admin/feed/preview'); return; } if ($this->cfg_css_async) { $this->_ccss = $this->cls('CSS')->prepare_ccss(); if (!$this->_ccss) { - Debug2::debug('[Optm] ❌ CCSS set to OFF due to CCSS not generated yet'); + self::debug('❌ CCSS set to OFF due to CCSS not generated yet'); $this->cfg_css_async = false; } elseif (strpos($this->_ccss, '<style id="litespeed-ccss" data-error') === 0) { - Debug2::debug('[Optm] ❌ CCSS set to OFF due to CCSS failed to generate'); + self::debug('❌ CCSS set to OFF due to CCSS failed to generate'); $this->cfg_css_async = false; } } @@ -429,7 +431,7 @@ private function _optimize() { // Shouldn't give any optm (defer/delay) @since 4.4 if ($this->_var_preserve_js) { $this->html_head .= '<script>var ' . implode(',', $this->_var_preserve_js) . ';</script>'; - Debug2::debug2('[Optm] Inline JS defer vars', $this->_var_preserve_js); + self::debug2('Inline JS defer vars', $this->_var_preserve_js); } // Append async compatibility lib to head @@ -439,7 +441,7 @@ private function _optimize() { $this->html_head .= $this->_build_js_inline(File::read(LSCWP_DIR . self::LIB_FILE_CSS_ASYNC), true); } else { $css_async_lib_url = LSWCP_PLUGIN_URL . self::LIB_FILE_CSS_ASYNC; - $this->html_head .= $this->_build_js_tag($css_async_lib_url, 'litespeed-css-async-lib'); // Don't exclude it from defer for now + $this->html_head .= $this->_build_js_tag($css_async_lib_url); // Don't exclude it from defer for now } } @@ -477,15 +479,33 @@ private function _optimize() { } // Replace html head part + $this->html_head_early = apply_filters('litespeed_optm_html_head_early', $this->html_head_early); + if ($this->html_head_early) { + // Put header content to be after charset + if (false !== strpos($this->content, '<meta charset')) { + self::debug('Put early optm data to be after <meta charset>'); + $this->content = preg_replace('#<meta charset([^>]*)>#isU', '<meta charset$1>' . $this->html_head_early, $this->content, 1); + } else { + self::debug('Put early optm data to be right after <head>'); + $this->content = preg_replace('#<head([^>]*)>#isU', '<head$1>' . $this->html_head_early, $this->content, 1); + } + } $this->html_head = apply_filters('litespeed_optm_html_head', $this->html_head); if ($this->html_head) { if (apply_filters('litespeed_optm_html_after_head', false)) { $this->content = str_replace('</head>', $this->html_head . '</head>', $this->content); } else { - // Put header content to be after charset - if (strpos($this->content, '<meta charset') !== false) { + // Put header content to dummy css position + if (false !== preg_match(self::DUMMY_CSS_REGEX, $this->content)) { + self::debug('Put optm data to dummy css location'); + $this->content = preg_replace( self::DUMMY_CSS_REGEX, $this->html_head, $this->content ); + } + // Fallback: try to be after charset + elseif (strpos($this->content, '<meta charset') !== false) { + self::debug('Put optm data to be after <meta charset>'); $this->content = preg_replace('#<meta charset([^>]*)>#isU', '<meta charset$1>' . $this->html_head, $this->content, 1); } else { + self::debug('Put optm data to be after <head>'); $this->content = preg_replace('#<head([^>]*)>#isU', '<head$1>' . $this->html_head, $this->content, 1); } } @@ -568,9 +588,9 @@ private function _async_ggfonts() { return; } - Debug2::debug2('[Optm] google fonts async found: ', $this->_ggfonts_urls); + self::debug2('google fonts async found: ', $this->_ggfonts_urls); - $html = '<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />'; + $this->html_head_early .= '<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />'; /** * Append fonts @@ -592,7 +612,7 @@ private function _async_ggfonts() { parse_str($qs, $qs); if (empty($qs['family'])) { - Debug2::debug('[Optm] ERR ggfonts failed to find family: ' . $v); + self::debug('ERR ggfonts failed to find family: ' . $v); continue; } @@ -610,7 +630,7 @@ private function _async_ggfonts() { // if webfontloader lib was loaded before WebFontConfig variable, call WebFont.load $script .= 'if ( typeof WebFont === "object" && typeof WebFont.load === "function" ) { WebFont.load( WebFontConfig ); }'; - $html .= $this->_build_js_inline($script); + $html = $this->_build_js_inline($script); // https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js $webfont_lib_url = LSWCP_PLUGIN_URL . self::LIB_FILE_WEBFONTLOADER; @@ -633,7 +653,7 @@ private function _font_optm() { return; } - Debug2::debug2('[Optm] google fonts optm ', $this->_ggfonts_urls); + self::debug2('google fonts optm ', $this->_ggfonts_urls); foreach ($this->_ggfonts_urls as $v) { if (strpos($v, 'display=')) { @@ -648,53 +668,48 @@ private function _font_optm() { /** * Prefetch DNS * - * @since 1.7.1 + * @since 1.7.1 DNS prefetch + * @since 5.6.1 DNS preconnect * @access private */ - private function _dns_prefetch_init() { + private function _dns_optm_init() { // Widely enable link DNS prefetch if (defined('LITESPEED_GUEST_OPTM') || $this->conf(self::O_OPTM_DNS_PREFETCH_CTRL)) { @header('X-DNS-Prefetch-Control: on'); } $this->dns_prefetch = $this->conf(self::O_OPTM_DNS_PREFETCH); - if (!$this->dns_prefetch) { + $this->dns_preconnect = $this->conf(self::O_OPTM_DNS_PRECONNECT); + if (!$this->dns_prefetch && !$this->dns_preconnect) { return; } if (function_exists('wp_resource_hints')) { - add_filter('wp_resource_hints', array( $this, 'dns_prefetch_filter' ), 10, 2); + add_filter('wp_resource_hints', array( $this, 'dns_optm_filter' ), 10, 2); } else { - add_action('litespeed_optm', array( $this, 'dns_prefetch_output' )); - } - } - - /** - * Preconnect init - * - * @since 5.6.1 - */ - private function _dns_preconnect_init() { - $this->dns_preconnect = $this->conf(self::O_OPTM_DNS_PRECONNECT); - if ($this->dns_preconnect) { - add_action('litespeed_optm', array( $this, 'dns_preconnect_output' )); + add_action('litespeed_optm', array( $this, 'dns_optm_output' )); } } /** - * Prefetch DNS hook for WP + * DNS optm hook for WP * * @since 1.7.1 * @access public */ - public function dns_prefetch_filter( $urls, $relation_type ) { - if ($relation_type !== 'dns-prefetch') { - return $urls; + public function dns_optm_filter( $urls, $relation_type ) { + if ('dns-prefetch' === $relation_type) { + foreach ($this->dns_prefetch as $v) { + if ($v) { + $urls[] = $v; + } + } } - - foreach ($this->dns_prefetch as $v) { - if ($v) { - $urls[] = $v; + if ('preconnect' === $relation_type) { + foreach ($this->dns_prefetch as $v) { + if ($v) { + $urls[] = $v; + } } } @@ -702,29 +717,21 @@ public function dns_prefetch_filter( $urls, $relation_type ) { } /** - * Prefetch DNS + * DNS optm output directly * - * @since 1.7.1 + * @since 1.7.1 DNS prefetch + * @since 5.6.1 DNS preconnect * @access public */ - public function dns_prefetch_output() { + public function dns_optm_output() { foreach ($this->dns_prefetch as $v) { if ($v) { - $this->html_head .= '<link rel="dns-prefetch" href="' . Str::trim_quotes($v) . '" />'; + $this->html_head_early .= '<link rel="dns-prefetch" href="' . Str::trim_quotes($v) . '" />'; } } - } - - /** - * Preconnect - * - * @since 5.6.1 - * @access public - */ - public function dns_preconnect_output() { foreach ($this->dns_preconnect as $v) { if ($v) { - $this->html_head .= '<link rel="preconnect" href="' . Str::trim_quotes($v) . '" />'; + $this->html_head_early .= '<link rel="preconnect" href="' . Str::trim_quotes($v) . '" crossorigin />'; } } } @@ -856,7 +863,7 @@ private function _parse_js() { $combine_ext_inl = $this->conf(self::O_OPTM_JS_COMB_EXT_INL); if (!apply_filters('litespeed_optm_js_comb_ext_inl', true)) { - Debug2::debug2('[Optm] js_comb_ext_inl bypassed via litespeed_optm_js_comb_ext_inl filter'); + self::debug2('js_comb_ext_inl bypassed via litespeed_optm_js_comb_ext_inl filter'); $combine_ext_inl = false; } @@ -904,7 +911,7 @@ private function _parse_js() { } } - Debug2::debug2('[Optm] _parse_js bypassed due to ' . ($js_excluded ? 'js files excluded [hit] ' . $js_excluded : 'external js')); + self::debug2('_parse_js bypassed due to ' . ($js_excluded ? 'js files excluded [hit] ' . $js_excluded : 'external js')); continue; } @@ -920,7 +927,7 @@ private function _parse_js() { } // Inline JS elseif (!empty($match[2])) { - // Debug2::debug( '🌹🌹🌹 ' . $match[2] . '🌹' ); + // self::debug( '🌹🌹🌹 ' . $match[2] . '🌹' ); // Exclude check $js_excluded = Utility::str_hit_array($match[2], $excludes); if ($js_excluded || !$combine_ext_inl) { @@ -931,7 +938,7 @@ private function _parse_js() { $this->content = str_replace($match[0], $deferred, $this->content); } } - Debug2::debug2('[Optm] _parse_js bypassed due to ' . ($js_excluded ? 'js excluded [hit] ' . $js_excluded : 'inline js')); + self::debug2('_parse_js bypassed due to ' . ($js_excluded ? 'js excluded [hit] ' . $js_excluded : 'inline js')); continue; } @@ -942,7 +949,7 @@ private function _parse_js() { } } else { // Compatibility to those who changed src to data-src already - Debug2::debug2('[Optm] No JS src or inline JS content'); + self::debug2('No JS src or inline JS content'); continue; } @@ -961,13 +968,13 @@ private function _parse_js() { */ private function _js_inline_defer( $con, $attrs = false, $minified = false ) { if (strpos($attrs, 'data-no-defer') !== false) { - Debug2::debug2('[Optm] bypass: attr api data-no-defer'); + self::debug2('bypass: attr api data-no-defer'); return false; } $hit = Utility::str_hit_array($con, $this->cfg_js_defer_exc); if ($hit) { - Debug2::debug2('[Optm] inline js defer excluded [setting] ' . $hit); + self::debug2('inline js defer excluded [setting] ' . $hit); return false; } @@ -1031,9 +1038,12 @@ private function _parse_css() { $excludes = apply_filters('litespeed_optimize_css_excludes', $this->conf(self::O_OPTM_CSS_EXC)); $ucss_file_exc_inline = apply_filters('litespeed_optimize_ucss_file_exc_inline', $this->conf(self::O_OPTM_UCSS_FILE_EXC_INLINE)); + // Append dummy css to exclude list + $excludes[] = 'litespeed-dummy.css'; + $combine_ext_inl = $this->conf(self::O_OPTM_CSS_COMB_EXT_INL); if (!apply_filters('litespeed_optm_css_comb_ext_inl', true)) { - Debug2::debug2('[Optm] css_comb_ext_inl bypassed via litespeed_optm_css_comb_ext_inl filter'); + self::debug2('css_comb_ext_inl bypassed via litespeed_optm_css_comb_ext_inl filter'); $combine_ext_inl = false; } @@ -1061,7 +1071,7 @@ private function _parse_css() { } if ($exclude = Utility::str_hit_array($match[0], $excludes)) { - Debug2::debug2('[Optm] _parse_css bypassed exclude ' . $exclude); + self::debug2('_parse_css bypassed exclude ' . $exclude); continue; } @@ -1077,7 +1087,7 @@ private function _parse_css() { // Check if need to remove this css if (Utility::str_hit_array($attrs['href'], $css_to_be_removed)) { - Debug2::debug('[Optm] rm css snippet ' . $attrs['href']); + self::debug('rm css snippet ' . $attrs['href']); // Delete this css snippet from orig html $this->content = str_replace($match[0], '', $this->content); @@ -1086,7 +1096,7 @@ private function _parse_css() { // Check if need to inline this css file if ($this->conf(self::O_OPTM_UCSS) && Utility::str_hit_array($attrs['href'], $ucss_file_exc_inline)) { - Debug2::debug('[Optm] ucss_file_exc_inline hit ' . $attrs['href']); + self::debug('ucss_file_exc_inline hit ' . $attrs['href']); // Replace this css to inline from orig html $inline_script = '<style>' . $this->__optimizer->load_file($attrs['href']) . '</style>'; $this->content = str_replace($match[0], $inline_script, $this->content); @@ -1107,7 +1117,7 @@ private function _parse_css() { } if ($this->cfg_ggfonts_rm || $this->cfg_ggfonts_async) { - Debug2::debug('[Optm] rm css snippet [Google fonts] ' . $attrs['href']); + self::debug('rm css snippet [Google fonts] ' . $attrs['href']); $this->content = str_replace($match[0], '', $this->content); continue; @@ -1125,7 +1135,7 @@ private function _parse_css() { $is_internal = Utility::is_internal_file($attrs['href']); $ext_excluded = !$combine_ext_inl && !$is_internal; if ($ext_excluded) { - Debug2::debug2('[Optm] Bypassed due to external link'); + self::debug2('Bypassed due to external link'); // Maybe defer if ($this->cfg_css_async) { $snippet = $this->_async_css($match[0]); @@ -1145,7 +1155,7 @@ private function _parse_css() { } else { // Inline style if (!$combine_ext_inl) { - Debug2::debug2('[Optm] Bypassed due to inline'); + self::debug2('Bypassed due to inline'); continue; } @@ -1195,12 +1205,12 @@ private function _async_css_list( $html_list, $src_list ) { */ private function _async_css( $ori ) { if (strpos($ori, 'data-asynced') !== false) { - Debug2::debug2('[Optm] bypass: attr data-asynced exist'); + self::debug2('bypass: attr data-asynced exist'); return $ori; } if (strpos($ori, 'data-no-async') !== false) { - Debug2::debug2('[Optm] bypass: attr api data-no-async'); + self::debug2('bypass: attr api data-no-async'); return $ori; } @@ -1222,18 +1232,18 @@ private function _async_css( $ori ) { */ private function _js_defer( $ori, $src ) { if (strpos($ori, ' async') !== false) { - $ori = preg_replace('# async(?:=([\'"])(?:[^\1]+)\1)?#isU', '', $ori); + $ori = preg_replace('# async(?:=([\'"])(?:[^\1]*?)\1)?#is', '', $ori); } if (strpos($ori, 'defer') !== false) { return false; } if (strpos($ori, 'data-deferred') !== false) { - Debug2::debug2('[Optm] bypass: attr data-deferred exist'); + self::debug2('bypass: attr data-deferred exist'); return false; } if (strpos($ori, 'data-no-defer') !== false) { - Debug2::debug2('[Optm] bypass: attr api data-no-defer'); + self::debug2('bypass: attr api data-no-defer'); return false; } @@ -1243,7 +1253,7 @@ private function _js_defer( $ori, $src ) { * @since 1.5 */ if (Utility::str_hit_array($src, $this->cfg_js_defer_exc)) { - Debug2::debug('[Optm] js defer exclude ' . $src); + self::debug('js defer exclude ' . $src); return false; } @@ -1271,11 +1281,11 @@ private function _js_delay( $ori, $src ) { return false; } if (strpos($ori, 'data-deferred') !== false) { - Debug2::debug2('[Optm] bypass: attr data-deferred exist'); + self::debug2('bypass: attr data-deferred exist'); return false; } if (strpos($ori, 'data-no-defer') !== false) { - Debug2::debug2('[Optm] bypass: attr api data-no-defer'); + self::debug2('bypass: attr api data-no-defer'); return false; } diff --git a/src/optimizer.cls.php b/src/optimizer.cls.php index 9fc67c5f3..96a651392 100644 --- a/src/optimizer.cls.php +++ b/src/optimizer.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The optimize4 class. * * @since 1.9 * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; diff --git a/src/placeholder.cls.php b/src/placeholder.cls.php index a4db0a860..14cd347b5 100644 --- a/src/placeholder.cls.php +++ b/src/placeholder.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The PlaceHolder class * * @since 3.0 * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; @@ -57,7 +56,7 @@ public function __construct() { public function init() { Debug2::debug2('[LQIP] init'); - add_action('litspeed_after_admin_init', array( $this, 'after_admin_init' )); + add_action('litespeed_after_admin_init', array( $this, 'after_admin_init' )); } /** diff --git a/src/purge.cls.php b/src/purge.cls.php index 7adf731c6..96d8db805 100644 --- a/src/purge.cls.php +++ b/src/purge.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The plugin purge class for X-LiteSpeed-Purge @@ -384,15 +385,39 @@ private function _purge_all_cssjs( $silence = false ) { * Purge opcode cache * * @since 1.8.2 + * @since 7.3 added test for opcode cache restriction * @access public */ public function purge_all_opcache( $silence = false ) { if (!Router::opcache_enabled()) { - self::debug('Failed to reset opcode cache due to opcache not enabled'); + self::debug('❌ Failed to reset OPcache due to OPcache not enabled'); if (!$silence) { - $msg = __('Opcode cache is not enabled.', 'litespeed-cache'); - Admin_Display::error($msg); + $msg = __('OPcache is not enabled.', 'litespeed-cache'); + !defined('LITESPEED_PURGE_SILENT') && Admin_Display::error($msg); + } + + return false; + } + + if (Router::opcache_restricted(__FILE__)) { + self::debug('❌ Failed to reset OPcache due to OPcache is restricted. File requesting the clear is not allowed.'); + + if (!$silence) { + $msg = sprintf(__('OPcache is restricted by %s setting.', 'litespeed-cache'), '<code>restrict_api</code>'); + !defined('LITESPEED_PURGE_SILENT') && Admin_Display::error($msg); + } + + return false; + } + + // Purge opcode cache + if (!opcache_reset()) { + self::debug('❌ Reset OPcache not worked'); + + if (!$silence) { + $msg = __('Reset the OPcache failed.', 'litespeed-cache'); + !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg); } return false; @@ -401,12 +426,10 @@ public function purge_all_opcache( $silence = false ) { // Action to run after opcache purge. do_action('litespeed_purged_all_opcache'); - // Purge opcode cache - opcache_reset(); - self::debug('Reset opcode cache'); + self::debug('Reset OPcache'); if (!$silence) { - $msg = __('Reset the entire opcode cache successfully.', 'litespeed-cache'); + $msg = __('Reset the entire OPcache successfully.', 'litespeed-cache'); !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg); } @@ -741,7 +764,7 @@ public function purge_tag( $val ) { return; } $term = get_term_by('slug', $val, 'post_tag'); - if ($term == 0) { + if ($term === false) { self::debug("$val tag not exist"); return; } diff --git a/src/report.cls.php b/src/report.cls.php index 06a08a17e..a30644fe4 100644 --- a/src/report.cls.php +++ b/src/report.cls.php @@ -1,12 +1,11 @@ <?php +// phpcs:ignoreFile /** * The report class * * @since 1.1.0 * @package LiteSpeed - * @subpackage LiteSpeed/src - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; diff --git a/src/rest.cls.php b/src/rest.cls.php index e65e5e06a..a66ff793b 100644 --- a/src/rest.cls.php +++ b/src/rest.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The REST related class. diff --git a/src/root.cls.php b/src/root.cls.php index 3f4583b29..500898787 100644 --- a/src/root.cls.php +++ b/src/root.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The abstract instance @@ -302,6 +303,58 @@ public function primary_overwritten( $id ) { return self::$_primary_options[$id]; } + /** + * Check if is overwritten by code filter + * + * @since 7.4 + */ + public function filter_overwritten( $id ) { + $cls_admin_display = Admin_Display::$settings_filters; + // Check if filter name is set. + if(!isset($cls_admin_display[$id]) || !isset($cls_admin_display[$id]['filter']) || is_array($cls_admin_display[$id]['filter']) ){ + return null; + } + + $val_setting = $this->conf($id, true); + // if setting not found + if( null === $val_setting ){ + $val_setting = ''; + } + + $val_filter = apply_filters($cls_admin_display[$id]['filter'], $val_setting ); + + if ($val_setting === $val_filter) { + // If the value is the same, return null. + return null; + } + + return $val_filter; + } + + /** + * Check if is overwritten by $SERVER variable + * + * @since 7.4 + */ + public function server_overwritten( $id ) { + $cls_admin_display = Admin_Display::$settings_filters; + if(!isset($cls_admin_display[$id]['filter'])){ + return null; + } + + if(!is_array($cls_admin_display[$id]['filter'])) { + $cls_admin_display[$id]['filter'] = array( $cls_admin_display[$id]['filter'] ); + } + + foreach( $cls_admin_display[$id]['filter'] as $variable ){ + if(isset($_SERVER[$variable])) { + return [ $variable , $_SERVER[$variable] ] ; + } + } + + return null; + } + /** * Get the list of configured options for the blog. * @@ -417,10 +470,6 @@ public static function ori_cls() { */ public static function name( $id ) { $name = strtolower(self::ori_cls()); - if ($name == 'conf2') { - // For a certain 3.7rc correction, can be dropped after v4 - $name = 'conf'; - } return 'litespeed.' . $name . '.' . $id; } diff --git a/src/router.cls.php b/src/router.cls.php index f06831103..a7d88e267 100644 --- a/src/router.cls.php +++ b/src/router.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The core plugin router class. @@ -6,7 +7,6 @@ * This generate the valid action. * * @since 1.1.0 - * @since 1.5 Moved into /inc */ namespace LiteSpeed; @@ -41,6 +41,7 @@ class Router extends Base { const ACTION_DEBUG2 = 'debug2'; const ACTION_CDN_CLOUDFLARE = 'CDN\Cloudflare'; const ACTION_ADMIN_DISPLAY = 'admin_display'; + const ACTION_TMP_DISABLE = 'tmp_disable'; // List all handlers here private static $_HANDLERS = array( @@ -358,7 +359,7 @@ public static function get_role( $uid = null ) { $user = get_userdata($uid); if (isset($user->roles) && is_array($user->roles)) { $tmp = array_values($user->roles); - $role = implode(',', $tmp); // Combine for PHP5.3 const comaptibility + $role = implode(',', $tmp); // Combine for PHP5.3 const compatibility } } Debug2::debug('[Router] get_role: ' . $role); @@ -587,6 +588,11 @@ private function verify_action() { $_can_option = current_user_can('manage_options'); switch ($action) { + case self::ACTION_TMP_DISABLE: // Disable LSC for 24H + Debug2::tmp_disable(); + Admin::redirect("?page=litespeed-toolbox#settings-debug"); + return; + case self::ACTION_SAVE_SETTINGS_NETWORK: // Save network settings if ($_can_network_option) { self::$_action = $action; @@ -749,6 +755,25 @@ public static function opcache_enabled() { return function_exists('opcache_reset') && ini_get('opcache.enable'); } + /** + * Check if opcode cache is restricted and file that is requesting. + * https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.restrict-api + * + * @since 7.3 + * @access public + */ + public static function opcache_restricted($file) + { + $restrict_value = ini_get('opcache.restrict_api'); + if ($restrict_value) { + if ( !$file || false === strpos($restrict_value, $file) ) { + return true; + } + } + + return false; + } + /** * Handle static files * diff --git a/src/str.cls.php b/src/str.cls.php index 8320d2852..842afb6e2 100644 --- a/src/str.cls.php +++ b/src/str.cls.php @@ -1,31 +1,44 @@ <?php - +// phpcs:ignoreFile /** * LiteSpeed String Operator Library Class * * @since 1.3 + * @package LiteSpeed */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Str + * + * Provides string manipulation utilities for LiteSpeed Cache. + * + * @since 1.3 + */ class Str { /** - * Translate QC HTML links from html. Convert `<a href="{#xxx#}">xxxx</a>` to `<a href="xxx">xxxx</a>` + * Translate QC HTML links from html. + * + * Converts `<a href="{#xxx#}">xxxx</a>` to `<a href="xxx">xxxx</a>`. * * @since 7.0 + * @access public + * @param string $html The HTML string to process. + * @return string The processed HTML string. */ public static function translate_qc_apis( $html ) { - preg_match_all('/<a href="{#(\w+)#}"/U', $html, $matches); - if (!$matches) { + preg_match_all( '/<a href="{#(\w+)#}"/U', $html, $matches ); + if ( ! $matches ) { return $html; } - foreach ($matches[0] as $k => $html_to_be_replaced) { - $link = '<a href="' . Utility::build_url(Router::ACTION_CLOUD, Cloud::TYPE_API, false, null, array( 'action2' => $matches[1][$k] )) . '"'; - $html = str_replace($html_to_be_replaced, $link, $html); + foreach ( $matches[0] as $k => $html_to_be_replaced ) { + $link = '<a href="' . Utility::build_url( Router::ACTION_CLOUD, Cloud::TYPE_API, false, null, array( 'action2' => $matches[1][ $k ] ) ) . '"'; + $html = str_replace( $html_to_be_replaced, $link, $html ); } return $html; } @@ -33,86 +46,98 @@ public static function translate_qc_apis( $html ) { /** * Return safe HTML * + * Sanitizes HTML to allow only specific tags and attributes. + * * @since 7.0 + * @access public + * @param string $html The HTML string to sanitize. + * @return string The sanitized HTML string. */ public static function safe_html( $html ) { $common_attrs = array( - 'style' => array(), - 'class' => array(), + 'style' => array(), + 'class' => array(), 'target' => array(), - 'src' => array(), - 'color' => array(), - 'href' => array(), + 'src' => array(), + 'color' => array(), + 'href' => array(), ); $tags = array( 'hr', 'h3', 'h4', 'h5', 'ul', 'li', 'br', 'strong', 'p', 'span', 'img', 'a', 'div', 'font' ); $allowed_tags = array(); - foreach ($tags as $tag) { - $allowed_tags[$tag] = $common_attrs; + foreach ( $tags as $tag ) { + $allowed_tags[ $tag ] = $common_attrs; } - return wp_kses($html, $allowed_tags); + return wp_kses( $html, $allowed_tags ); } /** * Generate random string * + * Creates a random string of specified length and character type. + * * @since 1.3 * @access public - * @param int $len Length of string - * @param int $type 1-Number 2-LowerChar 4-UpperChar - * @return string + * @param int $len Length of string. + * @param int $type Character type: 1-Number, 2-LowerChar, 4-UpperChar, 7-All. + * @return string Randomly generated string. */ public static function rrand( $len, $type = 7 ) { - switch ($type) { + switch ( $type ) { case 0: - $charlist = '012'; + $charlist = '012'; break; case 1: - $charlist = '0123456789'; + $charlist = '0123456789'; break; case 2: - $charlist = 'abcdefghijklmnopqrstuvwxyz'; + $charlist = 'abcdefghijklmnopqrstuvwxyz'; break; case 3: - $charlist = '0123456789abcdefghijklmnopqrstuvwxyz'; + $charlist = '0123456789abcdefghijklmnopqrstuvwxyz'; break; case 4: - $charlist = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charlist = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 5: - $charlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 6: - $charlist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charlist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 7: - $charlist = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charlist = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; } $str = ''; - $max = strlen($charlist) - 1; - for ($i = 0; $i < $len; $i++) { - $str .= $charlist[random_int(0, $max)]; + $max = strlen( $charlist ) - 1; + for ( $i = 0; $i < $len; $i++ ) { + $str .= $charlist[ random_int( 0, $max ) ]; } return $str; } /** - * Trim double quotes from a string to be used as a preformatted src in HTML. + * Trim double quotes from a string + * + * Removes double quotes from a string for use as a preformatted src in HTML. * * @since 6.5.3 + * @access public + * @param string $text The string to process. + * @return string The string with double quotes removed. */ - public static function trim_quotes( $string ) { - return str_replace('"', '', $string); + public static function trim_quotes( $text ) { + return str_replace( '"', '', $text ); } } diff --git a/src/tag.cls.php b/src/tag.cls.php index 4076ccab3..90d498495 100644 --- a/src/tag.cls.php +++ b/src/tag.cls.php @@ -1,10 +1,10 @@ <?php +// phpcs:ignoreFile /** * The plugin cache-tag class for X-LiteSpeed-Tag * * @since 1.1.3 - * @since 1.5 Moved into /inc */ namespace LiteSpeed; diff --git a/src/task.cls.php b/src/task.cls.php index 22aee6672..88a66db64 100644 --- a/src/task.cls.php +++ b/src/task.cls.php @@ -1,10 +1,10 @@ <?php +// phpcs:ignoreFile /** * The cron task class. * * @since 1.1.3 - * @since 1.5 Moved into /inc */ namespace LiteSpeed; @@ -224,7 +224,7 @@ public function lscache_cron_filter( $schedules ) { * @access public */ public function lscache_cron_filter_crawler( $schedules ) { - $CRAWLER_RUN_INTERVAL = defined('LITESPEED_CRAWLER_RUN_INTERVAL') ? LITESPEED_CRAWLER_RUN_INTERVAL : 600; + $CRAWLER_RUN_INTERVAL = defined('LITESPEED_CRAWLER_RUN_INTERVAL') ? constant('LITESPEED_CRAWLER_RUN_INTERVAL') : 600; // $wp_schedules = wp_get_schedules(); if (!array_key_exists(self::FILTER_CRAWLER, $schedules)) { // self::debug('Crawler cron log: cron filter '.$interval.' added'); diff --git a/src/tool.cls.php b/src/tool.cls.php index 1aceddfd8..bee77703d 100644 --- a/src/tool.cls.php +++ b/src/tool.cls.php @@ -1,18 +1,23 @@ <?php - +// phpcs:ignoreFile /** * The tools * * @since 3.0 * @package LiteSpeed - * @subpackage LiteSpeed/inc - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed; -defined('WPINC') || exit(); +defined( 'WPINC' ) || exit(); +/** + * Class Tool + * + * Provides utility functions for LiteSpeed Cache, including IP detection and heartbeat control. + * + * @since 3.0 + */ class Tool extends Root { const LOG_TAG = '[Tool]'; @@ -20,88 +25,95 @@ class Tool extends Root { /** * Get public IP * + * Retrieves the public IP address of the server. + * * @since 3.0 * @access public + * @return string The public IP address or an error message. */ public function check_ip() { - self::debug('✅ check_ip'); + self::debug( '✅ check_ip' ); - $response = wp_safe_remote_get('https://cyberpanel.sh/?ip', array( + $response = wp_safe_remote_get( 'https://cyberpanel.sh/?ip', array( 'headers' => array( 'User-Agent' => 'curl/8.7.1', ), - )); + ) ); - if (is_wp_error($response)) { - return __('Failed to detect IP', 'litespeed-cache'); + if ( is_wp_error( $response ) ) { + return esc_html__( 'Failed to detect IP', 'litespeed-cache' ); } - $ip = trim($response['body']); + $ip = trim( $response['body'] ); - self::debug('result [ip] ' . $ip); + self::debug( 'result [ip] ' . $ip ); - if (Utility::valid_ipv4($ip)) { + if ( Utility::valid_ipv4( $ip ) ) { return $ip; } - return __('Failed to detect IP', 'litespeed-cache'); + return esc_html__( 'Failed to detect IP', 'litespeed-cache' ); } /** * Heartbeat Control * - * NOTE: since WP4.9, there could be a core bug that sometimes the hook is not working. + * Configures WordPress heartbeat settings for frontend, backend, and editor. * * @since 3.0 * @access public */ public function heartbeat() { - add_action('wp_enqueue_scripts', array( $this, 'heartbeat_frontend' )); - add_action('admin_enqueue_scripts', array( $this, 'heartbeat_backend' )); - add_filter('heartbeat_settings', array( $this, 'heartbeat_settings' )); + add_action( 'wp_enqueue_scripts', array( $this, 'heartbeat_frontend' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'heartbeat_backend' ) ); + add_filter( 'heartbeat_settings', array( $this, 'heartbeat_settings' ) ); } /** * Heartbeat Control frontend control * + * Manages heartbeat settings for the frontend. + * * @since 3.0 * @access public */ public function heartbeat_frontend() { - if (!$this->conf(Base::O_MISC_HEARTBEAT_FRONT)) { + if ( ! $this->conf( Base::O_MISC_HEARTBEAT_FRONT ) ) { return; } - if (!$this->conf(Base::O_MISC_HEARTBEAT_FRONT_TTL)) { - wp_deregister_script('heartbeat'); - Debug2::debug('[Tool] Deregistered frontend heartbeat'); + if ( ! $this->conf( Base::O_MISC_HEARTBEAT_FRONT_TTL ) ) { + wp_deregister_script( 'heartbeat' ); + Debug2::debug( '[Tool] Deregistered frontend heartbeat' ); } } /** * Heartbeat Control backend control * + * Manages heartbeat settings for the backend and editor. + * * @since 3.0 * @access public */ public function heartbeat_backend() { - if ($this->_is_editor()) { - if (!$this->conf(Base::O_MISC_HEARTBEAT_EDITOR)) { + if ( $this->is_editor() ) { + if ( ! $this->conf( Base::O_MISC_HEARTBEAT_EDITOR ) ) { return; } - if (!$this->conf(Base::O_MISC_HEARTBEAT_EDITOR_TTL)) { - wp_deregister_script('heartbeat'); - Debug2::debug('[Tool] Deregistered editor heartbeat'); + if ( ! $this->conf( Base::O_MISC_HEARTBEAT_EDITOR_TTL ) ) { + wp_deregister_script( 'heartbeat' ); + Debug2::debug( '[Tool] Deregistered editor heartbeat' ); } } else { - if (!$this->conf(Base::O_MISC_HEARTBEAT_BACK)) { + if ( ! $this->conf( Base::O_MISC_HEARTBEAT_BACK ) ) { return; } - if (!$this->conf(Base::O_MISC_HEARTBEAT_BACK_TTL)) { - wp_deregister_script('heartbeat'); - Debug2::debug('[Tool] Deregistered backend heartbeat'); + if ( ! $this->conf( Base::O_MISC_HEARTBEAT_BACK_TTL ) ) { + wp_deregister_script( 'heartbeat' ); + Debug2::debug( '[Tool] Deregistered backend heartbeat' ); } } } @@ -109,37 +121,45 @@ public function heartbeat_backend() { /** * Heartbeat Control settings * + * Adjusts heartbeat interval settings based on configuration. + * * @since 3.0 * @access public + * @param array $settings Existing heartbeat settings. + * @return array Modified heartbeat settings. */ public function heartbeat_settings( $settings ) { // Check editor first to make frontend editor valid too - if ($this->_is_editor()) { - if ($this->conf(Base::O_MISC_HEARTBEAT_EDITOR)) { - $settings['interval'] = $this->conf(Base::O_MISC_HEARTBEAT_EDITOR_TTL); - Debug2::debug('[Tool] Heartbeat interval set to ' . $this->conf(Base::O_MISC_HEARTBEAT_EDITOR_TTL)); + if ( $this->is_editor() ) { + if ( $this->conf( Base::O_MISC_HEARTBEAT_EDITOR ) ) { + $settings['interval'] = $this->conf( Base::O_MISC_HEARTBEAT_EDITOR_TTL ); + Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->conf( Base::O_MISC_HEARTBEAT_EDITOR_TTL ) ); } - } elseif (!is_admin()) { - if ($this->conf(Base::O_MISC_HEARTBEAT_FRONT)) { - $settings['interval'] = $this->conf(Base::O_MISC_HEARTBEAT_FRONT_TTL); - Debug2::debug('[Tool] Heartbeat interval set to ' . $this->conf(Base::O_MISC_HEARTBEAT_FRONT_TTL)); + } elseif ( ! is_admin() ) { + if ( $this->conf( Base::O_MISC_HEARTBEAT_FRONT ) ) { + $settings['interval'] = $this->conf( Base::O_MISC_HEARTBEAT_FRONT_TTL ); + Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->conf( Base::O_MISC_HEARTBEAT_FRONT_TTL ) ); } - } elseif ($this->conf(Base::O_MISC_HEARTBEAT_BACK)) { - $settings['interval'] = $this->conf(Base::O_MISC_HEARTBEAT_BACK_TTL); - Debug2::debug('[Tool] Heartbeat interval set to ' . $this->conf(Base::O_MISC_HEARTBEAT_BACK_TTL)); + } elseif ( $this->conf( Base::O_MISC_HEARTBEAT_BACK ) ) { + $settings['interval'] = $this->conf( Base::O_MISC_HEARTBEAT_BACK_TTL ); + Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->conf( Base::O_MISC_HEARTBEAT_BACK_TTL ) ); } return $settings; } /** - * If is in editor + * Check if in editor + * + * Determines if the current request is within the WordPress editor. * * @since 3.0 * @access public + * @return bool True if in editor, false otherwise. */ - private function _is_editor() { - $res = is_admin() && Utility::str_hit_array($_SERVER['REQUEST_URI'], array( 'post.php', 'post-new.php' )); + public function is_editor() { + $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + $res = is_admin() && Utility::str_hit_array( $request_uri, array( 'post.php', 'post-new.php' ) ); - return apply_filters('litespeed_is_editor', $res); + return apply_filters( 'litespeed_is_editor', $res ); } } diff --git a/src/ucss.cls.php b/src/ucss.cls.php index d7c1e6a47..d5b0a7987 100644 --- a/src/ucss.cls.php +++ b/src/ucss.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The ucss class. diff --git a/src/utility.cls.php b/src/utility.cls.php index 2f14864e0..8b8ad546b 100644 --- a/src/utility.cls.php +++ b/src/utility.cls.php @@ -1,10 +1,10 @@ <?php +// phpcs:ignoreFile /** * The utility class. * * @since 1.1.5 - * @since 1.5 Moved into /inc */ namespace LiteSpeed; @@ -441,7 +441,7 @@ public static function url2uri( $url, $keep_qs = false ) { * * @since 3.0 * @access public - * @param string `https://aa.com/bbb/wp-content/upload/2018/08/test.jpg` or `/bbb/wp-content/upload/2018/08/test.jpg` + * @param string $url `https://aa.com/bbb/wp-content/upload/2018/08/test.jpg` or `/bbb/wp-content/upload/2018/08/test.jpg` * @return string `2018/08/test.jpg` */ public static function att_short_path( $url ) { @@ -544,8 +544,8 @@ public static function domain_const() { * * @since 1.3 * @access public - * @param string $content - * @param bool $type String handler type + * @param array|string $arr + * @param string|null $type String handler type * @return string|array */ public static function sanitize_lines( $arr, $type = null ) { @@ -693,7 +693,7 @@ public static function build_url( $action, $type = false, $is_ajax = false, $pag public static function internal( $host ) { if (!defined('LITESPEED_FRONTEND_HOST')) { if (defined('WP_HOME')) { - $home_host = WP_HOME; // Also think of `WP_SITEURL` + $home_host = constant('WP_HOME'); // Also think of `WP_SITEURL` } else { $home_host = get_option('home'); } @@ -773,7 +773,7 @@ public static function is_internal_file( $url, $addition_postfix = false ) { */ if (substr($url_parsed['path'], 0, 1) === '/') { if (defined('LITESPEED_WP_REALPATH')) { - $file_path_ori = $_SERVER['DOCUMENT_ROOT'] . LITESPEED_WP_REALPATH . $url_parsed['path']; + $file_path_ori = $_SERVER['DOCUMENT_ROOT'] . constant('LITESPEED_WP_REALPATH') . $url_parsed['path']; } else { $file_path_ori = $_SERVER['DOCUMENT_ROOT'] . $url_parsed['path']; } @@ -921,4 +921,38 @@ public static function chunk_placeholder( $data, $fields ) { return $q; } + + /** + * Prepare image sizes for optimization. + * + * @since 7.5 + * @access public + */ + public static function prepare_image_sizes_array( $detailed = false ) { + $image_sizes = wp_get_registered_image_subsizes(); + $sizes = []; + + foreach ( $image_sizes as $current_size_name => $current_size ) { + if( empty( $current_size['width'] ) && empty( $current_size['height'] ) ) continue; + + if( !$detailed ) { + $sizes[] = $current_size_name; + } + else{ + $label = $current_size['width'] . 'x' . $current_size['height']; + if( $current_size_name !== $label ){ + $label = ucfirst( $current_size_name ) . ' ( ' . $label . ' )'; + } + + $sizes[] = [ + "label" => $label, + "file_size" => $current_size_name, + "width" => $current_size['width'], + "height" => $current_size['height'], + ]; + } + } + + return $sizes; + } } diff --git a/src/vary.cls.php b/src/vary.cls.php index e7e18c65a..277c75637 100644 --- a/src/vary.cls.php +++ b/src/vary.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The plugin vary class to manage X-LiteSpeed-Vary @@ -750,11 +751,11 @@ private function _get_cookie_val( $key ) { * * @since 1.0.4 * @access private - * @param integer $val The value to update. - * @param integer $expire Expire time. - * @param boolean $path False if use wp root path as cookie path + * @param int|false $val The value to update. + * @param int $expire Expire time. + * @param bool $path False if use wp root path as cookie path */ - private function _cookie( $val = false, $expire = false, $path = false ) { + private function _cookie( $val = false, $expire = 0, $path = false ) { if (!$val) { $expire = 1; } diff --git a/src/vpi.cls.php b/src/vpi.cls.php index 1973a82bc..84f7970b3 100644 --- a/src/vpi.cls.php +++ b/src/vpi.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The viewport image class. @@ -40,6 +41,10 @@ public function add_to_queue() { global $wp; $request_url = home_url($wp->request); + if (!apply_filters('litespeed_vpi_should_queue', true, $request_url)) { + return; + } + $ua = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; // Store it to prepare for cron diff --git a/thirdparty/aelia-currencyswitcher.cls.php b/thirdparty/aelia-currencyswitcher.cls.php index 7c65252d5..d22ce01f9 100644 --- a/thirdparty/aelia-currencyswitcher.cls.php +++ b/thirdparty/aelia-currencyswitcher.cls.php @@ -1,12 +1,12 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the Aelia CurrencySwitcher plugin. * * @since 1.0.13 * @since 2.6 Removed hook_vary as OLS supports vary header already - * @package LiteSpeed_Cache + * @package LiteSpeed * @subpackage LiteSpeed_Cache/thirdparty - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed\Thirdparty; diff --git a/thirdparty/amp.cls.php b/thirdparty/amp.cls.php index 19f5c62d1..dec15f296 100644 --- a/thirdparty/amp.cls.php +++ b/thirdparty/amp.cls.php @@ -1,11 +1,11 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with AMP plugin. * * @since 2.9.8.6 - * @package LiteSpeed_Cache + * @package LiteSpeed * @subpackage LiteSpeed_Cache/thirdparty - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed\Thirdparty; diff --git a/thirdparty/autoptimize.cls.php b/thirdparty/autoptimize.cls.php index dbd3b8646..662b39967 100644 --- a/thirdparty/autoptimize.cls.php +++ b/thirdparty/autoptimize.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the Autoptimize plugin. * diff --git a/thirdparty/avada.cls.php b/thirdparty/avada.cls.php index 1d2e2a70a..ede7f389f 100644 --- a/thirdparty/avada.cls.php +++ b/thirdparty/avada.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the Avada plugin. * diff --git a/thirdparty/bbpress.cls.php b/thirdparty/bbpress.cls.php index 7ac31dbf1..ad10b77be 100644 --- a/thirdparty/bbpress.cls.php +++ b/thirdparty/bbpress.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the bbPress plugin. * diff --git a/thirdparty/beaver-builder.cls.php b/thirdparty/beaver-builder.cls.php index b1e8e283b..7f12c83c6 100644 --- a/thirdparty/beaver-builder.cls.php +++ b/thirdparty/beaver-builder.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the Beaver Builder plugin. * diff --git a/thirdparty/caldera-forms.cls.php b/thirdparty/caldera-forms.cls.php index 3b3be23b9..e65670eb1 100644 --- a/thirdparty/caldera-forms.cls.php +++ b/thirdparty/caldera-forms.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with Caldera Forms. * diff --git a/thirdparty/divi-theme-builder.cls.php b/thirdparty/divi-theme-builder.cls.php index 7d3132d79..b7f54463c 100644 --- a/thirdparty/divi-theme-builder.cls.php +++ b/thirdparty/divi-theme-builder.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with DIVI Theme. * diff --git a/thirdparty/elementor.cls.php b/thirdparty/elementor.cls.php index af8e67145..4c516bb9b 100644 --- a/thirdparty/elementor.cls.php +++ b/thirdparty/elementor.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the bbPress plugin. * diff --git a/thirdparty/entry.inc.php b/thirdparty/entry.inc.php index dc6bab63b..82e05bfcd 100644 --- a/thirdparty/entry.inc.php +++ b/thirdparty/entry.inc.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The registry for Third Party Plugins Integration files. * diff --git a/thirdparty/facetwp.cls.php b/thirdparty/facetwp.cls.php index c524fcf5c..41f1f1c38 100644 --- a/thirdparty/facetwp.cls.php +++ b/thirdparty/facetwp.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with FacetWP. * diff --git a/thirdparty/gravity-forms.cls.php b/thirdparty/gravity-forms.cls.php index 1be5f0a07..52c726fee 100644 --- a/thirdparty/gravity-forms.cls.php +++ b/thirdparty/gravity-forms.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with Gravity Forms. * diff --git a/thirdparty/litespeed-check.cls.php b/thirdparty/litespeed-check.cls.php index 004f643da..92095eac7 100644 --- a/thirdparty/litespeed-check.cls.php +++ b/thirdparty/litespeed-check.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * Check if any plugins that could conflict with LiteSpeed Cache are active. diff --git a/thirdparty/nextgengallery.cls.php b/thirdparty/nextgengallery.cls.php index 3c6f96f11..c8de9fd7d 100644 --- a/thirdparty/nextgengallery.cls.php +++ b/thirdparty/nextgengallery.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the NextGen Gallery plugin. * diff --git a/thirdparty/perfmatters.cls.php b/thirdparty/perfmatters.cls.php index 9de843694..c320fd20e 100644 --- a/thirdparty/perfmatters.cls.php +++ b/thirdparty/perfmatters.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the Perfmatters plugin. diff --git a/thirdparty/theme-my-login.cls.php b/thirdparty/theme-my-login.cls.php index e2f953e31..f5dbdd9e5 100644 --- a/thirdparty/theme-my-login.cls.php +++ b/thirdparty/theme-my-login.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the Theme My Login plugin. * diff --git a/thirdparty/user-switching.cls.php b/thirdparty/user-switching.cls.php index 3a246f786..f8e352eaf 100644 --- a/thirdparty/user-switching.cls.php +++ b/thirdparty/user-switching.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with User Switching. * diff --git a/thirdparty/wc-pdf-product-vouchers.cls.php b/thirdparty/wc-pdf-product-vouchers.cls.php index feef7e86a..6beceb495 100644 --- a/thirdparty/wc-pdf-product-vouchers.cls.php +++ b/thirdparty/wc-pdf-product-vouchers.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with WooCommerce PDF Product Vouchers. * diff --git a/thirdparty/wcml.cls.php b/thirdparty/wcml.cls.php index 943ee4337..d1ef7d081 100644 --- a/thirdparty/wcml.cls.php +++ b/thirdparty/wcml.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with WCML. * diff --git a/thirdparty/woo-paypal.cls.php b/thirdparty/woo-paypal.cls.php index 6c73b1ded..6a2671699 100644 --- a/thirdparty/woo-paypal.cls.php +++ b/thirdparty/woo-paypal.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with WooCommerce PayPal Checkout Gateway * diff --git a/thirdparty/woocommerce.cls.php b/thirdparty/woocommerce.cls.php index c9945f669..7352a95c9 100644 --- a/thirdparty/woocommerce.cls.php +++ b/thirdparty/woocommerce.cls.php @@ -1,13 +1,13 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the WooCommerce plugin. * * @since 1.0.5 * @since 1.6.6 Added function_exists check for compatibility - * @package LiteSpeed_Cache + * @package LiteSpeed * @subpackage LiteSpeed_Cache/thirdparty - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed\Thirdparty; @@ -16,6 +16,7 @@ use LiteSpeed\API; use LiteSpeed\Base; +use LiteSpeed\ESI; class WooCommerce extends Base { @@ -85,6 +86,11 @@ public function add_hooks() { if (function_exists('is_product') && is_product()) { add_filter('litespeed_esi_params', array( $this, 'add_post_id' ), 10, 2); } + + // #612331 - remove WooCommerce geolocation redirect on ESI page (PR#708) + if (!empty($_GET[ESI::QS_ACTION]) && !empty($_GET[ESI::QS_PARAMS])) { + remove_action( 'template_redirect', array( 'WC_Cache_Helper', 'geolocation_ajax_redirect' ), 10 ); + } } if (is_admin()) { diff --git a/thirdparty/woocommerce.content.tpl.php b/thirdparty/woocommerce.content.tpl.php index 5297136a0..c33fc99a5 100644 --- a/thirdparty/woocommerce.content.tpl.php +++ b/thirdparty/woocommerce.content.tpl.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile namespace LiteSpeed\Thirdparty; diff --git a/thirdparty/woocommerce.tab.tpl.php b/thirdparty/woocommerce.tab.tpl.php index c38a4a085..09c03f6f5 100644 --- a/thirdparty/woocommerce.tab.tpl.php +++ b/thirdparty/woocommerce.tab.tpl.php @@ -1,3 +1,11 @@ -<?php defined( 'WPINC' ) || exit; ?> +<?php +/** + * WooCommerce tab template for LiteSpeed Cache plugin. + * + * @package LiteSpeed + */ -<a class='litespeed-tab nav-tab' href='#woocommerce' data-litespeed-tab='woocommerce'><?php echo __( 'WooCommerce', 'litespeed-cache' ); ?></a> +defined( 'WPINC' ) || exit; +?> + +<a class='litespeed-tab nav-tab' href='#woocommerce' data-litespeed-tab='woocommerce'>WooCommerce</a> diff --git a/thirdparty/wp-polls.cls.php b/thirdparty/wp-polls.cls.php index 80fb3e131..8c95f1875 100644 --- a/thirdparty/wp-polls.cls.php +++ b/thirdparty/wp-polls.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the WP-Polls plugin. * diff --git a/thirdparty/wp-postratings.cls.php b/thirdparty/wp-postratings.cls.php index 3a76e8466..2e4b77317 100644 --- a/thirdparty/wp-postratings.cls.php +++ b/thirdparty/wp-postratings.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the WP-PostRatings plugin. * diff --git a/thirdparty/wpdiscuz.cls.php b/thirdparty/wpdiscuz.cls.php index 13d4d9536..b5f0c2fdf 100644 --- a/thirdparty/wpdiscuz.cls.php +++ b/thirdparty/wpdiscuz.cls.php @@ -1,11 +1,11 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with Wpdiscuz. * * @since 2.9.5 - * @package LiteSpeed_Cache + * @package LiteSpeed * @subpackage LiteSpeed_Cache/thirdparty - * @author LiteSpeed Technologies <info@litespeedtech.com> */ namespace LiteSpeed\Thirdparty; diff --git a/thirdparty/wplister.cls.php b/thirdparty/wplister.cls.php index 6e0c03be8..40f548056 100644 --- a/thirdparty/wplister.cls.php +++ b/thirdparty/wplister.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the WPLister plugin. * diff --git a/thirdparty/wpml.cls.php b/thirdparty/wpml.cls.php index 66adde6c3..d606f7ccb 100644 --- a/thirdparty/wpml.cls.php +++ b/thirdparty/wpml.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with WPML. * diff --git a/thirdparty/wptouch.cls.php b/thirdparty/wptouch.cls.php index 7b234d828..cb9d21ae5 100644 --- a/thirdparty/wptouch.cls.php +++ b/thirdparty/wptouch.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the WPTouch Mobile plugin. * diff --git a/thirdparty/yith-wishlist.cls.php b/thirdparty/yith-wishlist.cls.php index 6bdbfb145..d0901dad2 100644 --- a/thirdparty/yith-wishlist.cls.php +++ b/thirdparty/yith-wishlist.cls.php @@ -1,4 +1,5 @@ <?php +// phpcs:ignoreFile /** * The Third Party integration with the YITH WooCommerce Wishlist plugin. * diff --git a/tpl/banner/cloud_news.tpl.php b/tpl/banner/cloud_news.tpl.php index b8cd94fbc..cbafc08bc 100644 --- a/tpl/banner/cloud_news.tpl.php +++ b/tpl/banner/cloud_news.tpl.php @@ -15,7 +15,7 @@ <div class="litespeed-wrap notice notice-success litespeed-banner-promo-full"> <div class="litespeed-banner-promo-content"> - <h3 class="litespeed-banner-title litespeed-top15"><?php echo esc_html( $this->_summary['news.title'] ); ?></h3> + <h3 class="litespeed-banner-title litespeed-top15"><?php echo wp_kses_post( $this->_summary['news.title'] ); ?></h3> <div class="litespeed-banner-description" style="flex-direction: column;"> <div class="litespeed-banner-description-padding-right-15"> <p class="litespeed-banner-description-content"> diff --git a/tpl/cache/entry.tpl.php b/tpl/cache/entry.tpl.php index 57dc9f465..ac822babc 100644 --- a/tpl/cache/entry.tpl.php +++ b/tpl/cache/entry.tpl.php @@ -12,6 +12,56 @@ defined( 'WPINC' ) || exit; +if ( $this->_is_network_admin ) { + $menu_list = array( + 'cache' => __( 'Cache', 'litespeed-cache' ), + 'purge' => __( 'Purge', 'litespeed-cache' ), + 'excludes' => __( 'Excludes', 'litespeed-cache' ), + 'object' => __( 'Object', 'litespeed-cache' ), + 'browser' => __( 'Browser', 'litespeed-cache' ), + 'advanced' => __( 'Advanced', 'litespeed-cache' ), + ); +?> + +<div class="wrap"> + <h1 class="litespeed-h1"> + <?php esc_html_e( 'LiteSpeed Cache Network Cache Settings', 'litespeed-cache' ); ?> + </h1> + <span class="litespeed-desc"> + <?php echo esc_html( 'v' . Core::VER ); ?> + </span> + <hr class="wp-header-end"> +</div> + +<div class="litespeed-wrap"> + <h2 class="litespeed-header nav-tab-wrapper"> + <?php GUI::display_tab_list( $menu_list ); ?> + </h2> + <div class="litespeed-body"> + <?php $this->cache_disabled_warning(); ?> + + <?php + $this->form_action( Router::ACTION_SAVE_SETTINGS_NETWORK ); + + foreach ( $menu_list as $k => $val ) { + $k_escaped = esc_attr( $k ); + ?> + <div data-litespeed-layout="<?php echo esc_html( $k_escaped ); ?>"> + <?php + require LSCWP_DIR . "tpl/cache/network_settings-$k.tpl.php"; + ?> + </div> + <?php + } + + $this->form_end(); + ?> + </div> +</div> +<?php + return; +} + $menu_list = array( 'cache' => __( 'Cache', 'litespeed-cache' ), 'ttl' => __( 'TTL', 'litespeed-cache' ), diff --git a/tpl/cache/entry_network.tpl.php b/tpl/cache/entry_network.tpl.php deleted file mode 100644 index 77810b9ee..000000000 --- a/tpl/cache/entry_network.tpl.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -/** - * LiteSpeed Cache Network Cache Settings - * - * Displays the network cache settings page with tabbed navigation for LiteSpeed Cache. - * - * @package LiteSpeed - * @since 1.0.0 - */ - -namespace LiteSpeed; - -defined( 'WPINC' ) || exit; - -$menu_list = array( - 'cache' => __( 'Cache', 'litespeed-cache' ), - 'purge' => __( 'Purge', 'litespeed-cache' ), - 'excludes' => __( 'Excludes', 'litespeed-cache' ), - 'object' => __( 'Object', 'litespeed-cache' ), - 'browser' => __( 'Browser', 'litespeed-cache' ), - 'advanced' => __( 'Advanced', 'litespeed-cache' ), -); -?> - -<div class="wrap"> - <h1 class="litespeed-h1"> - <?php esc_html_e( 'LiteSpeed Cache Network Cache Settings', 'litespeed-cache' ); ?> - </h1> - <span class="litespeed-desc"> - <?php echo esc_html( 'v' . Core::VER ); ?> - </span> - <hr class="wp-header-end"> -</div> - -<div class="litespeed-wrap"> - <h2 class="litespeed-header nav-tab-wrapper"> - <?php GUI::display_tab_list( $menu_list ); ?> - </h2> - <div class="litespeed-body"> - <?php $this->cache_disabled_warning(); ?> - - <?php - $this->form_action( Router::ACTION_SAVE_SETTINGS_NETWORK ); - - foreach ( $menu_list as $k => $val ) { - $k_escaped = esc_attr( $k ); - ?> - <div data-litespeed-layout="<?php echo esc_html( $k_escaped ); ?>"> - <?php - require LSCWP_DIR . "tpl/cache/network_settings-$k.tpl.php"; - ?> - </div> - <?php - } - - $this->form_end( true ); - ?> - </div> -</div> \ No newline at end of file diff --git a/tpl/cache/settings-cache.tpl.php b/tpl/cache/settings-cache.tpl.php index 4520378d5..ac03ca836 100644 --- a/tpl/cache/settings-cache.tpl.php +++ b/tpl/cache/settings-cache.tpl.php @@ -32,8 +32,8 @@ <div class="litespeed-desc"> <?php printf( - /* translators: %1$s: Opening link tag, %2$s: Closing link tag */ - esc_html__( 'Please visit the %1$sInformation%2$s page on how to test the cache.', 'litespeed-cache' ), + /* translators: %s: Link tags */ + esc_html__( 'Please visit the %sInformation%s page on how to test the cache.', 'litespeed-cache' ), '<a href="https://docs.litespeedtech.com/lscache/lscwp/installation/#testing" target="_blank" rel="noopener">', '</a>' ); diff --git a/tpl/cache/settings-esi.tpl.php b/tpl/cache/settings-esi.tpl.php index 3099874d7..a61016d7e 100644 --- a/tpl/cache/settings-esi.tpl.php +++ b/tpl/cache/settings-esi.tpl.php @@ -93,21 +93,17 @@ <?php $this->title( $option_id ); ?> </th> <td> - <div class="litespeed-row-flex"> - <div> - <?php $this->build_textarea( $option_id ); ?> - </div> - <div class="litespeed-width-3-10"> - <p class="litespeed-desc"> - <?php echo esc_html__( 'The list will be merged with the predefined nonces in your local data file.', 'litespeed-cache' ); ?> - <?php echo esc_html__( 'The latest data file is', 'litespeed-cache' ); ?>: <a href="https://github.com/litespeedtech/lscache_wp/blob/master/data/esi.nonces.txt" target="_blank">https://github.com/litespeedtech/lscache_wp/blob/master/data/esi.nonces.txt</a> - <br><span class="litespeed-success"> - <?php echo esc_html__( 'API', 'litespeed-cache' ); ?>: - <?php printf( esc_html__( 'Filter %s is supported.', 'litespeed-cache' ), '<code>litespeed_esi_nonces</code>' ); ?> - </span> - </p> - </div> + <div> + <?php $this->build_textarea( $option_id ); ?> </div> + <p class="litespeed-desc"> + <?php echo esc_html__( 'The list will be merged with the predefined nonces in your local data file.', 'litespeed-cache' ); ?> + <?php echo esc_html__( 'The latest data file is', 'litespeed-cache' ); ?>: <a href="https://github.com/litespeedtech/lscache_wp/blob/master/data/esi.nonces.txt" target="_blank">https://github.com/litespeedtech/lscache_wp/blob/master/data/esi.nonces.txt</a> + <br><span class="litespeed-success"> + <?php echo esc_html__( 'API', 'litespeed-cache' ); ?>: + <?php printf( esc_html__( 'Filter %s is supported.', 'litespeed-cache' ), '<code>litespeed_esi_nonces</code>' ); ?> + </span> + </p> <div class="litespeed-desc"> <?php echo esc_html__( 'The above nonces will be converted to ESI automatically.', 'litespeed-cache' ); ?> <?php Doc::one_per_line(); ?> diff --git a/tpl/cache/settings_inc.browser.tpl.php b/tpl/cache/settings_inc.browser.tpl.php index 862166824..ba15bc1fe 100644 --- a/tpl/cache/settings_inc.browser.tpl.php +++ b/tpl/cache/settings_inc.browser.tpl.php @@ -42,8 +42,8 @@ <?php Doc::notice_htaccess(); ?><br> <?php printf( - /* translators: %1$s: Opening link tag, %2$s: Closing link tag */ - esc_html__( 'You can turn on browser caching in server admin too. %1$sLearn more about LiteSpeed browser cache settings%2$s.', 'litespeed-cache' ), + /* translators: %s: Link tags */ + esc_html__( 'You can turn on browser caching in server admin too. %sLearn more about LiteSpeed browser cache settings%s.', 'litespeed-cache' ), '<a href="https://docs.litespeedtech.com/lscache/lscwp/cache/#how-to-set-it-up" target="_blank" rel="noopener">', '</a>' ); diff --git a/tpl/cdn/qc.tpl.php b/tpl/cdn/qc.tpl.php index 756ce991b..92f12f1b8 100644 --- a/tpl/cdn/qc.tpl.php +++ b/tpl/cdn/qc.tpl.php @@ -75,7 +75,8 @@ <p> <?php printf( - esc_html__( 'Best available WordPress performance, globally fast TTFB, easy setup, and %1$smore%2$s!', 'litespeed-cache' ), + /* translators: %s: Link tags */ + esc_html__( 'Best available WordPress performance, globally fast TTFB, easy setup, and %smore%s!', 'litespeed-cache' ), '<a href="https://www.quic.cloud/quic-cloud-services-and-features/litespeed-cache-service/" target="_blank" rel="noopener">', '</a>' ); diff --git a/tpl/crawler/blacklist.tpl.php b/tpl/crawler/blacklist.tpl.php index f3513b296..9c0a36ac1 100644 --- a/tpl/crawler/blacklist.tpl.php +++ b/tpl/crawler/blacklist.tpl.php @@ -12,6 +12,7 @@ $crawler_summary = Crawler::get_summary(); $__map = Crawler_Map::cls(); +$__admin_display = Admin_Display::cls(); $list = $__map->list_blacklist( 30 ); $count = $__map->count_blacklist(); $pagination = Utility::pagination( $count, 30 ); @@ -25,7 +26,7 @@ <h3 class="litespeed-title"> <?php esc_html_e( 'Blocklist', 'litespeed-cache' ); ?> - <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/crawler/#blacklist-tab' ); ?> + <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/crawler/#blocklist-tab' ); ?> </h3> <?php echo esc_html__( 'Total', 'litespeed-cache' ) . ': ' . esc_html( $count ); ?> @@ -81,6 +82,7 @@ ?> </span> </p> +<?php $__admin_display->_check_overwritten( 'crawler-blocklist' ); ?> <p> <i class="litespeed-dot litespeed-bg-default"></i> = <?php esc_html_e( 'Not blocklisted', 'litespeed-cache' ); ?><br> <i class="litespeed-dot litespeed-bg-warning"></i> = <?php esc_html_e( 'Blocklisted due to not cacheable', 'litespeed-cache' ); ?><br> diff --git a/tpl/crawler/entry.tpl.php b/tpl/crawler/entry.tpl.php index 6fe144b51..9c174c4fe 100644 --- a/tpl/crawler/entry.tpl.php +++ b/tpl/crawler/entry.tpl.php @@ -10,12 +10,12 @@ defined( 'WPINC' ) || exit; -$menu_list = array( +$menu_list = [ 'summary' => esc_html__( 'Summary', 'litespeed-cache' ), 'map' => esc_html__( 'Map', 'litespeed-cache' ), 'blacklist' => esc_html__( 'Blocklist', 'litespeed-cache' ), 'settings' => esc_html__( 'Settings', 'litespeed-cache' ), -); +]; ?> <div class="wrap"> diff --git a/tpl/crawler/summary.tpl.php b/tpl/crawler/summary.tpl.php index 450e04094..a9290e5d6 100644 --- a/tpl/crawler/summary.tpl.php +++ b/tpl/crawler/summary.tpl.php @@ -30,17 +30,17 @@ ) . '</span>'; } -$CRAWLER_RUN_INTERVAL = defined( 'LITESPEED_CRAWLER_RUN_INTERVAL' ) ? LITESPEED_CRAWLER_RUN_INTERVAL : 600; -if ( $CRAWLER_RUN_INTERVAL > 0 ) : +$crawler_run_interval = defined( 'LITESPEED_CRAWLER_RUN_INTERVAL' ) ? LITESPEED_CRAWLER_RUN_INTERVAL : 600; +if ( $crawler_run_interval > 0 ) : $recurrence = ''; - $hours = (int) floor( $CRAWLER_RUN_INTERVAL / 3600 ); + $hours = (int) floor( $crawler_run_interval / 3600 ); if ( $hours ) { $recurrence .= sprintf( $hours > 1 ? esc_html__( '%d hours', 'litespeed-cache' ) : esc_html__( '%d hour', 'litespeed-cache' ), $hours ); } - $minutes = (int) floor( ( $CRAWLER_RUN_INTERVAL % 3600 ) / 60 ); + $minutes = (int) floor( ( $crawler_run_interval % 3600 ) / 60 ); if ( $minutes ) { $recurrence .= ' '; $recurrence .= sprintf( @@ -62,7 +62,8 @@ <p> <?php printf( - esc_html__( 'See %1$sIntroduction for Enabling the Crawler%2$s for detailed information.', 'litespeed-cache' ), + /* translators: %s: Link tags */ + esc_html__( 'See %sIntroduction for Enabling the Crawler%s for detailed information.', 'litespeed-cache' ), '<a href="https://docs.litespeedtech.com/lscache/lscwp/admin/#enabling-and-limiting-the-crawler" target="_blank" rel="noopener">', '</a>' ); @@ -256,7 +257,8 @@ <div> <?php printf( - esc_html__( 'Please see %1$sHooking WP-Cron Into the System Task Scheduler%2$s to learn how to create the system cron task.', 'litespeed-cache' ), + /* translators: %s: Link tags */ + esc_html__( 'Please see %sHooking WP-Cron Into the System Task Scheduler%s to learn how to create the system cron task.', 'litespeed-cache' ), '<a href="https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/" target="_blank" rel="noopener">', '</a>' ); @@ -270,10 +272,10 @@ <h3 class="litespeed-title"><?php esc_html_e( 'Watch Crawler Status', 'litespeed-cache' ); ?></h3> <?php -$ajaxUrl = $__crawler->json_path(); -if ( $ajaxUrl ) : +$ajax_url = $__crawler->json_path(); +if ( $ajax_url ) : ?> - <input type="button" id="litespeed-crawl-url-btn" value="<?php esc_attr_e( 'Show crawler status', 'litespeed-cache' ); ?>" class="button button-secondary" data-url="<?php echo esc_url( $ajaxUrl ); ?>" /> + <input type="button" id="litespeed-crawl-url-btn" value="<?php esc_attr_e( 'Show crawler status', 'litespeed-cache' ); ?>" class="button button-secondary" data-url="<?php echo esc_url( $ajax_url ); ?>" /> <div class="litespeed-shell litespeed-hide"> <div class="litespeed-shell-header-bar"></div> <div class="litespeed-shell-header"> diff --git a/tpl/dash/entry.tpl.php b/tpl/dash/entry.tpl.php index 5d9ad1533..4a0bc3e72 100644 --- a/tpl/dash/entry.tpl.php +++ b/tpl/dash/entry.tpl.php @@ -38,7 +38,7 @@ <?php foreach ( $menu_list as $tab_key => $tab_val ) { echo '<div data-litespeed-layout="' . esc_attr( $tab_key ) . '">'; - require LSCWP_DIR . 'tpl/dash/' . sanitize_file_name( $tab_key ) . '.tpl.php'; + require LSCWP_DIR . 'tpl/dash/' . $tab_key . '.tpl.php'; echo '</div>'; } ?> diff --git a/tpl/db_optm/entry.tpl.php b/tpl/db_optm/entry.tpl.php index 849293be3..9660f6155 100644 --- a/tpl/db_optm/entry.tpl.php +++ b/tpl/db_optm/entry.tpl.php @@ -38,7 +38,7 @@ <?php foreach ( $menu_list as $tab_key => $tab_val ) { echo '<div data-litespeed-layout="' . esc_attr( $tab_key ) . '">'; - require LSCWP_DIR . 'tpl/db_optm/' . sanitize_file_name( $tab_key ) . '.tpl.php'; + require LSCWP_DIR . 'tpl/db_optm/' . $tab_key . '.tpl.php'; echo '</div>'; } ?> diff --git a/tpl/db_optm/manage.tpl.php b/tpl/db_optm/manage.tpl.php index 590f751c8..9e6e779d5 100644 --- a/tpl/db_optm/manage.tpl.php +++ b/tpl/db_optm/manage.tpl.php @@ -103,7 +103,7 @@ <?php echo esc_html( $v['title'] ); ?> <span class="litespeed-panel-counter<?php echo $v['count'] > 0 ? '-red' : ''; ?>">(<?php echo esc_html( $v['count'] ); ?><?php echo DB_Optm::hide_more() ? '+' : ''; ?>)</span> </div> - <span class="litespeed-panel-para"><?php echo esc_html( $v['desc'] ); ?></span> + <span class="litespeed-panel-para"><?php echo wp_kses_post( $v['desc'] ); ?></span> </section> <section class="litespeed-panel-wrapper-top-right"> <span class="litespeed-panel-top-right-icon<?php echo $v['count'] > 0 ? '-cross' : '-tick'; ?>"></span> @@ -134,10 +134,10 @@ ?> <tr> <td><?php echo esc_html( $k + 1 ); ?></td> - <td><?php echo esc_html( $v->TABLE_NAME ); ?></td> - <td><?php echo esc_html( $v->ENGINE ); ?></td> + <td><?php echo esc_html( $v->table_name ); ?></td> + <td><?php echo esc_html( $v->engine ); ?></td> <td> - <a href="<?php echo esc_url( Utility::build_url( Router::ACTION_DB_OPTM, DB_Optm::TYPE_CONV_TB, false, false, array( 'tb' => $v->TABLE_NAME ) ) ); ?>"> + <a href="<?php echo esc_url( Utility::build_url( Router::ACTION_DB_OPTM, DB_Optm::TYPE_CONV_TB, false, false, array( 'tb' => $v->table_name ) ) ); ?>"> <?php esc_html_e( 'Convert to InnoDB', 'litespeed-cache' ); ?> </a> </td> diff --git a/tpl/general/entry.tpl.php b/tpl/general/entry.tpl.php index 2b791c0ab..26d3a7dd7 100644 --- a/tpl/general/entry.tpl.php +++ b/tpl/general/entry.tpl.php @@ -45,7 +45,7 @@ <?php foreach ( $menu_list as $menu_key => $val ) { echo '<div data-litespeed-layout="' . esc_attr( $menu_key ) . '">'; - require LSCWP_DIR . 'tpl/general/' . sanitize_file_name( $menu_key ) . '.tpl.php'; + require LSCWP_DIR . 'tpl/general/' . $menu_key . '.tpl.php'; echo '</div>'; } ?> diff --git a/tpl/general/network_settings.tpl.php b/tpl/general/network_settings.tpl.php index ac5f7308b..de214f682 100644 --- a/tpl/general/network_settings.tpl.php +++ b/tpl/general/network_settings.tpl.php @@ -43,5 +43,5 @@ </tbody></table> <?php -$this->form_end( true ); +$this->form_end(); ?> \ No newline at end of file diff --git a/tpl/general/online.tpl.php b/tpl/general/online.tpl.php index 436794364..cb1e7c181 100644 --- a/tpl/general/online.tpl.php +++ b/tpl/general/online.tpl.php @@ -33,7 +33,7 @@ <p> <?php $has_service = false; - foreach ( Cloud::$SERVICES as $svc ) { + foreach ( Cloud::$services as $svc ) { if ( isset( $cloud_summary[ 'server.' . $svc ] ) ) { $has_service = true; printf( diff --git a/tpl/general/settings.tpl.php b/tpl/general/settings.tpl.php index ffec94acd..7167b53fe 100644 --- a/tpl/general/settings.tpl.php +++ b/tpl/general/settings.tpl.php @@ -15,7 +15,7 @@ $cloud_instance = Cloud::cls(); $cloud_summary = Cloud::get_summary(); -$ajax_url_getIP = function_exists('get_rest_url') ? get_rest_url(null, 'litespeed/v1/tool/check_ip') : '/'; +$ajax_url_get_ip = function_exists('get_rest_url') ? get_rest_url(null, 'litespeed/v1/tool/check_ip') : '/'; $this->form_action(); ?> @@ -136,7 +136,7 @@ $('#litespeed_get_ip').on('click', function (e) { console.log('[litespeed] get server IP'); $.ajax({ - url: '<?php echo $ajax_url_getIP; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>', + url: '<?php echo $ajax_url_get_ip; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>', dataType: 'json', beforeSend: function (xhr) { xhr.setRequestHeader('X-WP-Nonce', '<?php echo esc_js(wp_create_nonce('wp_rest')); ?>'); diff --git a/tpl/img_optm/entry.tpl.php b/tpl/img_optm/entry.tpl.php index 52b24c38a..f7fb683a9 100644 --- a/tpl/img_optm/entry.tpl.php +++ b/tpl/img_optm/entry.tpl.php @@ -44,7 +44,7 @@ <?php foreach ( $menu_list as $menu_key => $val ) { echo '<div data-litespeed-layout="' . esc_attr( $menu_key ) . '">'; - require LSCWP_DIR . 'tpl/img_optm/' . sanitize_file_name( $menu_key ) . '.tpl.php'; + require LSCWP_DIR . 'tpl/img_optm/' . $menu_key . '.tpl.php'; echo '</div>'; } ?> diff --git a/tpl/img_optm/network_settings.tpl.php b/tpl/img_optm/network_settings.tpl.php index 9b54c8cc3..65021caf4 100644 --- a/tpl/img_optm/network_settings.tpl.php +++ b/tpl/img_optm/network_settings.tpl.php @@ -26,4 +26,4 @@ </tbody></table> <?php -$this->form_end( true ); \ No newline at end of file +$this->form_end(); \ No newline at end of file diff --git a/tpl/img_optm/settings.tpl.php b/tpl/img_optm/settings.tpl.php index ac08ef6cb..5813a0cb1 100644 --- a/tpl/img_optm/settings.tpl.php +++ b/tpl/img_optm/settings.tpl.php @@ -83,6 +83,32 @@ </td> </tr> + <tr> + <th> + <?php + $option_id = Base::O_IMG_OPTM_SIZES_SKIPPED; + $image_sizes = Utility::prepare_image_sizes_array(true); + $option_value = $this->conf( $option_id ); + ?> + <?php $this->title( $option_id ); ?> + </th> + <td> + <?php if ( count($image_sizes) > 0 ) : ?> + <?php + foreach ( $image_sizes as $current_size ) { + $checked = false === array_search( $current_size['file_size'], $option_value, true ); + $this->build_checkbox( $option_id . '[]', esc_html( $current_size['label'] ), $checked, $current_size['file_size'] ); + } + ?> + <?php else : ?> + <p><?php esc_html_e( 'No sizes found.', 'litespeed-cache' ); ?></p> + <?php endif; ?> + <div class="litespeed-desc"> + <?php esc_html_e( 'Choose which image sizes to optimize.', 'litespeed-cache' ); ?> + </div> + </td> + </tr> + <tr> <th> <?php $option_id = Base::O_IMG_OPTM_EXIF; ?> @@ -137,7 +163,7 @@ <?php $this->build_switch( $option_id ); ?> <div class="litespeed-desc"> <?php printf( esc_html__( 'Enable replacement of WebP/AVIF in %s elements that were generated outside of WordPress logic.', 'litespeed-cache' ), '<code>srcset</code>' ); ?> - <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/imageopt/#webp-for-extra-srcset' ); ?> + <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/imageopt/#webpavif-for-extra-srcset' ); ?> </div> </td> </tr> diff --git a/tpl/img_optm/summary.tpl.php b/tpl/img_optm/summary.tpl.php index 0aeaf961c..0aa291fa4 100644 --- a/tpl/img_optm/summary.tpl.php +++ b/tpl/img_optm/summary.tpl.php @@ -332,8 +332,9 @@ </div> <div class="inside litespeed-postbox-footer litespeed-postbox-footer--compact litespeed-desc"> <?php - printf( - esc_html__( 'Results can be checked in %1$sMedia Library%2$s.', 'litespeed-cache' ), + printf( + /* translators: %s: Link tags */ + esc_html__( 'Results can be checked in %sMedia Library%s.', 'litespeed-cache' ), '<a href="upload.php?mode=list">', '</a>' ); @@ -373,7 +374,7 @@ </div> <div class="inside litespeed-postbox-footer litespeed-postbox-footer--compact"> <p> - <a href="<?php echo esc_url( Utility::build_url( Router::ACTION_IMG_OPTM, Img_Optm::TYPE_DESTROY ) ); ?>" class="litespeed-link-with-icon litespeed-danger" data-litespeed-cfm="<?php esc_html_e( 'Are you sure to destroy all optimized images?', 'litespeed-cache' ); ?>"> + <a href="<?php echo esc_url( Utility::build_url( Router::ACTION_IMG_OPTM, Img_Optm::TYPE_DESTROY ) ); ?>" class="litespeed-link-with-icon litespeed-danger" data-litespeed-cfm="<?php esc_html_e( 'Are you sure to destroy all optimized images?', 'litespeed-cache' ); ?>" id="litespeed-imageopt-destroy"> <span class="dashicons dashicons-dismiss"></span><?php esc_html_e( 'Destroy All Optimization Data', 'litespeed-cache' ); ?> </a> </p> diff --git a/tpl/inc/disabled_all.php b/tpl/inc/disabled_all.php deleted file mode 100644 index 97eefb547..000000000 --- a/tpl/inc/disabled_all.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * LiteSpeed Cache Disable All Features Notice - * - * Displays a warning notice about conflicting .htaccess rules from other plugins that may interfere with LiteSpeed Cache. - * - * @package LiteSpeed - * @since 1.0.0 - */ - -namespace LiteSpeed; - -defined( 'WPINC' ) || exit; - -$error_message = esc_html__( 'Disable All Features', 'litespeed-cache' ); - -// Other plugin left cache expired rules in .htaccess which will cause conflicts -echo wp_kses_post( Admin_Display::build_notice( Admin_Display::NOTICE_RED, $error_message ) ); diff --git a/tpl/inc/modal.deactivation.php b/tpl/inc/modal.deactivation.php new file mode 100644 index 000000000..60a79db89 --- /dev/null +++ b/tpl/inc/modal.deactivation.php @@ -0,0 +1,140 @@ +<?php +/** + * LiteSpeed Cache Deactivation Modal + * + * Renders the deactivation modal interface for LiteSpeed Cache, allowing users to send reason of deactivation. + * + * @package LiteSpeed + * @since 7.3 + */ + +namespace LiteSpeed; + +defined( 'WPINC' ) || exit; + +// Modal data +$_title = esc_html__('Deactivate LiteSpeed Cache', 'litespeed'); +$_id = 'litespeed-modal-deactivate'; + +$reasons = array( + array( + 'value' => 'Temporary', + 'text' => esc_html__('The deactivation is temporary', 'litespeed-cache'), + 'id' => 'temp', + 'selected' => true, + ), + array( + 'value' => 'Performance worse', + 'text' => esc_html__('Site performance is worse', 'litespeed-cache'), + 'id' => 'performance', + ), + array( + 'value' => 'Plugin complicated', + 'text' => esc_html__('Plugin is too complicated', 'litespeed-cache'), + 'id' => 'complicated', + ), + array( + 'value' => 'Other', + 'text' => esc_html__('Other', 'litespeed-cache'), + 'id' => 'other', + ), +); +?> +<div style="display: none"> + <div id="litespeed-deactivation" class="iziModal"> + <div id="litespeed-modal-deactivate"> + <form id="litespeed-deactivation-form" method="post"> + <p><?php esc_attr_e('Why are you deactivating the plugin?', 'litespeed-cache'); ?></p> + <div class="deactivate-reason-wrapper"> + <?php foreach ($reasons as $reason) : ?> + <label for="litespeed-deactivate-reason-<?php esc_attr_e( $reason['id'] ); ?>"> + <input type="radio" id="litespeed-deactivate-reason-<?php esc_attr_e( $reason['id'] ); ?>" value="<?php esc_attr_e( $reason['value'] ); ?>" + <?php isset($reason['selected']) && $reason['selected'] ? ' checked="checked"' : ''; ?> name="litespeed-reason" /> + <?php esc_html_e( $reason['text'] ); ?> + </label> + <?php endforeach; ?> + </div> + <div class="deactivate-clear-settings-wrapper"> + <i style="font-size: 0.9em;"> + <?php + esc_html_e('On uninstall, all plugin settings will be deleted.', 'litespeed-cache'); + ?> + </i> + <br /> + <i style="font-size: 0.9em;"> + + <?php + printf( + esc_html__('If you have used Image Optimization, please %sDestroy All Optimization Data%s first. NOTE: this does not remove your optimized images.', 'litespeed-cache'), + '<a href="admin.php?page=litespeed-img_optm#litespeed-imageopt-destroy" target="_blank">', + '</a>' + ); + ?> + </i> + </div> + <div class="deactivate-actions"> + <input type="submit" id="litespeed-deactivation-form-submit" class="button button-primary" value="<?php esc_attr_e('Deactivate', 'litespeed-cache'); ?>" title="<?php esc_attr_e('Deactivate plugin', 'litespeed-cache'); ?>" /> + <input type="button" id="litespeed-deactivation-form-cancel" class="button litespeed-btn-warning" value="<?php esc_attr_e('Cancel', 'litespeed-cache'); ?>" title="<?php esc_attr_e('Close popup', 'litespeed-cache'); ?>" /> + </div> + </form> + </div> + </div> +</div> +<script> + (function ($) { + 'use strict'; + jQuery(document).ready(function () { + var lscId = '<?php echo home_url(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>'; + var modalesc_attr_element = $('#litespeed-deactivation'); + var deactivateesc_attr_element = $('#deactivate-litespeed-cache'); + + if (deactivateesc_attr_element.length > 0 && modalesc_attr_element.length > 0) { + // Variables + var modal_formElement = $('#litespeed-deactivation-form'); + + deactivateesc_attr_element.on('click', function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + modal_formElement.attr('action', decodeURI($(this).attr('href'))); + modalesc_attr_element.iziModal({ + radius: '.5rem', + width: 550, + autoOpen: true, + }); + }); + + $(document).on('submit', '#litespeed-deactivation-form', function (e) { + e.preventDefault(); + $('#litespeed-deactivation-form-submit').attr('disabled', true); + var container = $('#litespeed-deactivation-form'); + + // Save selected data + var data = { + id: lscId, + siteLink: window.location.hostname, + reason: $(container).find('[name=litespeed-reason]:checked').val() + }; + + $.ajax({ + url: 'https://wpapi.quic.cloud/survey', + dataType: 'json', + method: 'POST', + cache: false, + data: data, + success: function (data) { + console.log('QC data sent.'); + }, + error: function (xhr, error) { + console.log('Error sending data to QC.'); + }, + }); + + $('#litespeed-deactivation-form')[0].submit(); + }); + $(document).on('click', '#litespeed-deactivation-form-cancel', function (e) { + modalesc_attr_element.iziModal('close'); + }); + } + }); + })(jQuery); +</script> diff --git a/tpl/inc/show_display_installed.php b/tpl/inc/show_display_installed.php index 6fb899571..2b442f7d6 100644 --- a/tpl/inc/show_display_installed.php +++ b/tpl/inc/show_display_installed.php @@ -33,7 +33,8 @@ esc_html__( 'Create a post, make sure the front page is accurate.', 'litespeed-cache' ) ); $buf .= sprintf( - esc_html__( 'If there are any questions, the team is always happy to answer any questions on the %1$ssupport forum%2$s.', 'litespeed-cache' ), + /* translators: %s: Link tags */ + esc_html__( 'If there are any questions, the team is always happy to answer any questions on the %ssupport forum%s.', 'litespeed-cache' ), '<a href="https://wordpress.org/support/plugin/litespeed-cache" rel="noopener noreferrer" target="_blank">', '</a>' ); diff --git a/tpl/inc/show_error_cookie.php b/tpl/inc/show_error_cookie.php index fcd6278d5..2a326f872 100644 --- a/tpl/inc/show_error_cookie.php +++ b/tpl/inc/show_error_cookie.php @@ -17,7 +17,11 @@ ' ' . esc_html__('If the login cookie was recently changed in the settings, please log out and back in.', 'litespeed-cache') . ' ' . - sprintf(esc_html__('If not, please verify the setting in the <a href="%1$s">Advanced tab</a>.', 'litespeed-cache'), esc_url(admin_url('admin.php?page=litespeed-cache#advanced'))); + sprintf( + esc_html__('If not, please verify the setting in the %sAdvanced tab%s.', 'litespeed-cache'), + "<a href='" . esc_url(admin_url('admin.php?page=litespeed-cache#advanced')) . '">', + '</a>' + ); if (LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS') { $err .= ' ' . esc_html__('If using OpenLiteSpeed, the server must be restarted once for the changes to take effect.', 'litespeed-cache'); diff --git a/tpl/optimax/entry.tpl.php b/tpl/optimax/entry.tpl.php new file mode 100644 index 000000000..83f17f1fa --- /dev/null +++ b/tpl/optimax/entry.tpl.php @@ -0,0 +1,53 @@ +<?php +/** + * LiteSpeed Cache OptimaX + * + * Manages the OptimaX interface for LiteSpeed Cache. + * + * @package LiteSpeed + * @since 8.0 + */ + +namespace LiteSpeed; + +defined( 'WPINC' ) || exit; + +$menu_list = array( + 'summary' => esc_html__( 'OptimaX Summary', 'litespeed-cache' ), + 'settings' => esc_html__( 'OptimaX Settings', 'litespeed-cache' ), +); + +if ( is_network_admin() ) { + $menu_list = array( + 'network_settings' => esc_html__( 'OptimaX Settings', 'litespeed-cache' ), + ); +} + +?> + +<div class="wrap"> + <h1 class="litespeed-h1"> + <?php esc_html_e( 'LiteSpeed Cache OptimaX', 'litespeed-cache' ); ?> + </h1> + <span class="litespeed-desc"> + v<?php echo esc_html( Core::VER ); ?> + </span> + <hr class="wp-header-end"> +</div> + +<div class="litespeed-wrap"> + <h2 class="litespeed-header nav-tab-wrapper"> + <?php GUI::display_tab_list( $menu_list ); ?> + </h2> + + <div class="litespeed-body"> + <?php + foreach ( $menu_list as $menu_key => $val ) { + echo '<div data-litespeed-layout="' . esc_attr( $menu_key ) . '">'; + require LSCWP_DIR . 'tpl/optimax/' . $menu_key . '.tpl.php'; + echo '</div>'; + } + ?> + </div> + +</div> \ No newline at end of file diff --git a/tpl/optimax/settings.tpl.php b/tpl/optimax/settings.tpl.php new file mode 100644 index 000000000..9859812f1 --- /dev/null +++ b/tpl/optimax/settings.tpl.php @@ -0,0 +1,43 @@ +<?php +/** + * LiteSpeed Cache OptimaX Settings + * + * Manages OptimaX settings for LiteSpeed Cache. + * + * @package LiteSpeed + * @since 8.0 + */ + +namespace LiteSpeed; + +defined( 'WPINC' ) || exit; + +$this->form_action(); +?> + +<h3 class="litespeed-title-short"> + <?php esc_html_e( 'OptimaX Settings', 'litespeed-cache' ); ?> + <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/imageopt/#image-optimization-settings-tab' ); ?> +</h3> + +<table class="wp-list-table striped litespeed-table"> + <tbody> + + <tr> + <th> + <?php $option_id = Base::O_OPTIMAX; ?> + <?php $this->title( $option_id ); ?> + </th> + <td> + <?php $this->build_switch( $option_id ); ?> + <div class="litespeed-desc"> + <?php esc_html_e( 'Turn on OptimaX. This will automatically request your pages OptimaX result via cron job.', 'litespeed-cache' ); ?> + </div> + </td> + </tr> + + </tbody> +</table> + +<?php +$this->form_end(); diff --git a/tpl/optimax/summary.tpl.php b/tpl/optimax/summary.tpl.php new file mode 100644 index 000000000..ecbe58561 --- /dev/null +++ b/tpl/optimax/summary.tpl.php @@ -0,0 +1,38 @@ +<?php +/** + * LiteSpeed Cache OptimaX Summary + * + * Manages the OX summary interface for LiteSpeed Cache. + * + * @package LiteSpeed + * @since 8.0 + */ + +namespace LiteSpeed; + +defined( 'WPINC' ) || exit; + +?> +<div class="litespeed-flex-container litespeed-column-with-boxes"> + <div class="litespeed-width-7-10 litespeed-column-left litespeed-image-optim-summary-wrapper"> + <div class="litespeed-image-optim-summary"> + <h3> + Coming soon + </h3> + </div> + </div> + + <div class="litespeed-width-3-10 litespeed-column-right"> + <div class="postbox litespeed-postbox litespeed-postbox-imgopt-info"> + <div class="inside"> + <h3 class="litespeed-title"> + Placeholder + </h3> + + <div class="litespeed-flex-container"> + ... Placeholder ... + </div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/tpl/page_optm/entry.tpl.php b/tpl/page_optm/entry.tpl.php index 04682649a..4e6351b3f 100644 --- a/tpl/page_optm/entry.tpl.php +++ b/tpl/page_optm/entry.tpl.php @@ -55,7 +55,7 @@ foreach ( $menu_list as $tab_key => $tab_val ) { ?> <div data-litespeed-layout='<?php echo esc_attr( $tab_key ); ?>'> - <?php require LSCWP_DIR . 'tpl/page_optm/' . sanitize_file_name( $tab_key ) . '.tpl.php'; ?> + <?php require LSCWP_DIR . 'tpl/page_optm/' . $tab_key . '.tpl.php'; ?> </div> <?php } diff --git a/tpl/page_optm/settings_css.tpl.php b/tpl/page_optm/settings_css.tpl.php index 20841f5d3..5ce1ecfc2 100644 --- a/tpl/page_optm/settings_css.tpl.php +++ b/tpl/page_optm/settings_css.tpl.php @@ -12,6 +12,7 @@ defined( 'WPINC' ) || exit; +$__admin_display = Admin_Display::cls(); $css_summary = CSS::get_summary(); $ucss_summary = UCSS::get_summary(); $closest_server_ucss = Cloud::get_summary( 'server.' . Cloud::SVC_UCSS ); @@ -84,6 +85,7 @@ <br /><?php esc_html_e( 'Automatic generation of unique CSS is in the background via a cron-based queue.', 'litespeed-cache' ); ?> <br /> <font class="litespeed-success"><?php esc_html_e( 'API', 'litespeed-cache' ); ?>: <?php printf( esc_html__( 'Filter %s available for UCSS per page type generation.', 'litespeed-cache' ), '<code>add_filter( "litespeed_ucss_per_pagetype", "__return_true" );</code>' ); ?></font> + <?php $__admin_display->_check_overwritten( 'optm-ucss_per_pagetype' ); ?> <?php if ( $this->conf( Base::O_OPTM_UCSS ) && ! $this->conf( Base::O_OPTM_CSS_COMB ) ) : ?> <br /> diff --git a/tpl/page_optm/settings_media.tpl.php b/tpl/page_optm/settings_media.tpl.php index 52db8775a..903a8772f 100644 --- a/tpl/page_optm/settings_media.tpl.php +++ b/tpl/page_optm/settings_media.tpl.php @@ -12,12 +12,14 @@ defined( 'WPINC' ) || exit; +$__admin_display = Admin_Display::cls(); $placeholder_summary = Placeholder::get_summary(); - -$closest_server = Cloud::get_summary( 'server.' . Cloud::SVC_LQIP ); +$closest_server = Cloud::get_summary( 'server.' . Cloud::SVC_LQIP ); $lqip_queue = $this->load_queue( 'lqip' ); +$scaled_size = apply_filters( 'big_image_size_threshold', 2560 ) . 'px'; + ?> <h3 class="litespeed-title-short"> @@ -256,6 +258,7 @@ <?php esc_html_e( 'API', 'litespeed-cache' ); ?>: <?php printf( esc_html__( 'Use %1$s to bypass remote image dimension check when %2$s is ON.', 'litespeed-cache' ), '<code>add_filter( "litespeed_media_ignore_remote_missing_sizes", "__return_true" );</code>', '<code>' . esc_html( Lang::title( Base::O_MEDIA_ADD_MISSING_SIZES ) ) . '</code>' ); ?> </font> + <?php $__admin_display->_check_overwritten( Base::O_MEDIA_ADD_MISSING_SIZES ); ?> </div> </td> </tr> @@ -274,5 +277,38 @@ </div> </td> </tr> + + <tr> + <th> + <?php $option_id = Base::O_MEDIA_AUTO_RESCALE_ORI; ?> + <?php $this->title( $option_id ); ?> + </th> + <td> + <?php $this->build_switch( $option_id ); ?> + <div class="litespeed-desc"> + <?php esc_html_e( 'Automatically replace large images with scaled versions.', 'litespeed-cache' ); ?> + <?php esc_html_e( 'Scaled size threshold', 'litespeed-cache' ); ?>: <code><?php echo wp_kses_post( $scaled_size ); ?></code> + <br /> + <span class="litespeed-success"> + API: + <?php + printf( + esc_html__( 'Filter %s available to change threshold.', 'litespeed-cache' ), + '<code>big_image_size_threshold</code>' + ); + ?> + <a href="https://developer.wordpress.org/reference/hooks/big_image_size_threshold/" target="_blank" class="litespeed-learn-more"> + <?php esc_html_e('Learn More', 'litespeed-cache'); ?> + </a> + </span> + + <br /> + <font class="litespeed-danger"> + 🚨 + <?php esc_html_e( 'This is irreversible.', 'litespeed-cache' ); ?> + </font> + </div> + </td> + </tr> </tbody> </table> \ No newline at end of file diff --git a/tpl/presets/standard.tpl.php b/tpl/presets/standard.tpl.php index f867fd83a..9edf2d8c1 100644 --- a/tpl/presets/standard.tpl.php +++ b/tpl/presets/standard.tpl.php @@ -22,7 +22,7 @@ ), 'footer' => array( esc_html__( 'This no-risk preset is appropriate for all websites. Good for new users, simple websites, or cache-oriented development.', 'litespeed-cache' ), - esc_html__( 'A Domain Key is not required to use this preset. Only basic caching features are enabled.', 'litespeed-cache' ), + esc_html__( 'A QUIC.cloud connection is not required to use this preset. Only basic caching features are enabled.', 'litespeed-cache' ), ), ), 'basic' => array( @@ -34,7 +34,7 @@ ), 'footer' => array( esc_html__( 'This low-risk preset introduces basic optimizations for speed and user experience. Appropriate for enthusiastic beginners.', 'litespeed-cache' ), - esc_html__( 'A Domain Key is required to use this preset. Includes optimizations known to improve site score in page speed measurement tools.', 'litespeed-cache' ), + esc_html__( 'A QUIC.cloud connection is required to use this preset. Includes optimizations known to improve site score in page speed measurement tools.', 'litespeed-cache' ), ), ), 'advanced' => array( @@ -53,7 +53,7 @@ ), 'footer' => array( esc_html__( 'This preset is good for most websites, and is unlikely to cause conflicts. Any CSS or JS conflicts may be resolved with Page Optimization > Tuning tools.', 'litespeed-cache' ), - esc_html__( 'A Domain Key is required to use this preset. Includes many optimizations known to improve page speed scores.', 'litespeed-cache' ), + esc_html__( 'A QUIC.cloud connection is required to use this preset. Includes many optimizations known to improve page speed scores.', 'litespeed-cache' ), ), ), 'aggressive' => array( @@ -67,7 +67,7 @@ ), 'footer' => array( esc_html__( 'This preset might work out of the box for some websites, but be sure to test! Some CSS or JS exclusions may be necessary in Page Optimization > Tuning.', 'litespeed-cache' ), - esc_html__( 'A Domain Key is required to use this preset. Includes many optimizations known to improve page speed scores.', 'litespeed-cache' ), + esc_html__( 'A QUIC.cloud connection is required to use this preset. Includes many optimizations known to improve page speed scores.', 'litespeed-cache' ), ), ), 'extreme' => array( @@ -82,7 +82,7 @@ ), 'footer' => array( esc_html__( 'This preset almost certainly will require testing and exclusions for some CSS, JS and Lazy Loaded images. Pay special attention to logos, or HTML-based slider images.', 'litespeed-cache' ), - esc_html__( 'A Domain Key is required to use this preset. Enables the maximum level of optimizations for improved page speed scores.', 'litespeed-cache' ), + esc_html__( 'A QUIC.cloud connection is required to use this preset. Enables the maximum level of optimizations for improved page speed scores.', 'litespeed-cache' ), ), ), ); diff --git a/tpl/toolbox/beta_test.tpl.php b/tpl/toolbox/beta_test.tpl.php index 4650dbb43..9d5a82a34 100644 --- a/tpl/toolbox/beta_test.tpl.php +++ b/tpl/toolbox/beta_test.tpl.php @@ -14,6 +14,11 @@ // List of available public versions $v_list = array( + '7.5.0.1', + '7.5', + '7.4', + '7.3.0.1', + '7.3', '7.2', '7.1', '7.0.1', @@ -31,6 +36,13 @@ <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/toolbox/#beta-test-tab' ); ?> </h3> + <?php if ( defined( 'LITESPEED_DISABLE_ALL' ) && LITESPEED_DISABLE_ALL ) : ?> + <div class="litespeed-callout notice notice-warning inline"> + <h4><?php esc_html_e( 'NOTICE:', 'litespeed-cache' ); ?></h4> + <p><?php esc_html_e( 'LiteSpeed Cache is disabled. This functionality will not work.', 'litespeed-cache' ); ?></p> + </div> + <?php endif; ?> + <div class="litespeed-desc"> <?php esc_html_e( 'Use this section to switch plugin versions. To beta test a GitHub commit, enter the commit URL in the field below.', 'litespeed-cache' ); ?> </div> diff --git a/tpl/toolbox/purge.tpl.php b/tpl/toolbox/purge.tpl.php index 33d6a99b9..078275851 100644 --- a/tpl/toolbox/purge.tpl.php +++ b/tpl/toolbox/purge.tpl.php @@ -227,11 +227,43 @@ </form> <script> (function ($) { + function setCookie(name, value, days) { + var expires = ""; + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + document.cookie = name + "=" + (value || "") + expires + "; path=/; SameSite=Strict"; + } + + function getCookie(name) { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) == 0) { + return c.substring(nameEQ.length, c.length); + } + } + return null; + } + jQuery(document).ready(function () { + var savedPurgeBy = getCookie('litespeed_purgeby_option'); + if (savedPurgeBy) { + $('input[name="<?php echo esc_attr( Admin_Display::PURGEBYOPT_SELECT ); ?>"][value="' + savedPurgeBy + '"]').prop('checked', true); + $('[data-purgeby]').addClass('litespeed-hide'); + $('[data-purgeby="' + savedPurgeBy + '"]').removeClass('litespeed-hide'); + } // Manage page -> purge by $('[name=purgeby]').on('change', function (event) { $('[data-purgeby]').addClass('litespeed-hide'); $('[data-purgeby=' + this.value + ']').removeClass('litespeed-hide'); + setCookie('litespeed_purgeby_option', this.value, 30); }); }); })(jQuery); diff --git a/tpl/toolbox/report.tpl.php b/tpl/toolbox/report.tpl.php index b39eb4674..b5b99c536 100644 --- a/tpl/toolbox/report.tpl.php +++ b/tpl/toolbox/report.tpl.php @@ -105,7 +105,15 @@ <?php esc_html_e( 'To grant wp-admin access to the LiteSpeed Support Team, please generate a passwordless link for the current logged-in user to be sent with the report.', 'litespeed-cache' ); ?> <?php if ( $dologin_link ) : ?> <br /><strong>🚨 <?php esc_html_e( 'Please do NOT share the above passwordless link with anyone.', 'litespeed-cache' ); ?></strong> - <strong><?php printf( esc_html__( 'Generated links may be managed under %1$sSettings%2$s.', 'litespeed-cache' ), '<a href="' . esc_url( menu_page_url( 'dologin', false ) ) . '#pswdless">', '</a>' ); ?></strong> + <strong> + <?php + printf( + /* translators: %s: Link tags */ + esc_html__( 'Generated links may be managed under %sSettings%s.', 'litespeed-cache' ), + '<a href="' . esc_url( menu_page_url( 'dologin', false ) ) . '#pswdless">', + '</a>' ); + ?> + </strong> <?php endif; ?> </div> </td> diff --git a/tpl/toolbox/settings-debug.tpl.php b/tpl/toolbox/settings-debug.tpl.php index 4dc98d307..45422229e 100644 --- a/tpl/toolbox/settings-debug.tpl.php +++ b/tpl/toolbox/settings-debug.tpl.php @@ -27,6 +27,30 @@ <?php esc_html_e( 'View Site Before Cache', 'litespeed-cache' ); ?> </a> + +<?php +$temp_disabled_time = $this->conf( Base::DEBUG_TMP_DISABLE ); +$temp_disabled = Debug2::is_tmp_disable(); +if ( !$temp_disabled ) { +?> + <a href="<?php echo wp_kses_post( Utility::build_url(Router::ACTION_TMP_DISABLE, false, false, '_ori') ); ?>" class="button litespeed-btn-danger"> + <?php esc_html_e( 'Disable All Features for 24 Hours', 'litespeed-cache' ); ?> + </a> +<?php +} else { + $date = wp_date( get_option('date_format') . ' ' . get_option( 'time_format' ), $temp_disabled_time ); +?> + <a href="<?php echo wp_kses_post( Utility::build_url(Router::ACTION_TMP_DISABLE, false, false, '_ori') ); ?>" class="button litespeed-btn-warning"> + <?php esc_html_e( 'Remove `Disable All Feature` Flag Now', 'litespeed-cache' ); ?> + </a> + <div class="litespeed-callout notice notice-warning inline"> + <h4><?php esc_html_e( 'NOTICE', 'litespeed-cache' ); ?></h4> + <p><?php echo wp_kses_post( sprintf ( __( 'LiteSpeed Cache is temporarily disabled until: %s.', 'litespeed-cache' ), '<strong>' . $date . '</strong>' ) ); ?></p> + </div> +<?php +} +?> + <h3 class="litespeed-title-short"> <?php esc_html_e( 'Debug Settings', 'litespeed-cache' ); ?> <?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/toolbox/#debug-settings-tab' ); ?> diff --git a/typos.toml b/typos.toml new file mode 100644 index 000000000..fdb720c4e --- /dev/null +++ b/typos.toml @@ -0,0 +1,7 @@ +[files] +extend-exclude = [ + "**", + "!cli/**", + "!tpl/**", + "!autoload.php" +] From d7fd1783a214e1b26a8bafcfe0653a9b1294284f Mon Sep 17 00:00:00 2001 From: Tim LSC <timotei.litespeed@gmail.com> Date: Wed, 17 Sep 2025 10:36:23 +0300 Subject: [PATCH 12/12] Formatting = fixes --- src/cdn.cls.php | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/cdn.cls.php b/src/cdn.cls.php index 9b6701d6a..46012cc42 100644 --- a/src/cdn.cls.php +++ b/src/cdn.cls.php @@ -140,25 +140,7 @@ public function init() { // Add IMAGES to rewrite if CDN Mapping setting is enabled if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG])) { - $add_images_type = apply_filters('litespeed_cdn_add_filetypes_image', array('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif')); - foreach ($add_images_type as $ext) { - $this->_cfg_cdn_mapping[$ext] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG]; - } - } - - // Add CSS to rewrite if CDN Mapping setting is enabled - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS])) { - $this->_cfg_cdn_mapping['.css'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_CSS]; - } - - // Add JS to rewrite if CDN Mapping setting is enabled - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS])) { - $this->_cfg_cdn_mapping['.js'] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_JS]; - } - - // Add IMAGES to rewrite if CDN Mapping setting is enabled - if (!empty($this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG])) { - $add_images_type = apply_filters('litespeed_cdn_add_filetypes_image', array('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif')); + $add_images_type = apply_filters( 'litespeed_cdn_add_filetypes_image', array( '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.avif' ) ); foreach ($add_images_type as $ext) { $this->_cfg_cdn_mapping[$ext] = $this->_cfg_cdn_mapping[Base::CDN_MAPPING_INC_IMG]; }