PluginProbe ʕ •ᴥ•ʔ
10Web Booster – Website speed optimization, Cache & Page Speed optimizer / trunk
10Web Booster – Website speed optimization, Cache & Page Speed optimizer vtrunk
2.33.0 2.30.5 2.30.7 2.30.9 2.31.10 2.31.8 2.32.11 2.32.21 2.32.3 2.32.4 2.32.7 2.6.31 2.6.40 2.6.42 2.6.7 2.7.37 2.7.44 2.7.47 2.8.18 2.8.19 2.8.32 2.8.34 2.8.35 2.9.23 2.9.24 2.9.25 2.9.27 v2.27.4 trunk 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.17 2.0.18 2.0.21 2.0.22 2.0.25 2.0.26 2.0.27 2.0.3 2.0.7 2.0.9 2.10.46 2.10.65 2.10.66 2.10.68 2.11.41 2.11.42 2.11.43 2.12.15 2.12.21 2.12.22 2.12.23 2.12.26 2.13.37 2.13.40 2.13.41 2.13.42 2.13.44 2.13.45 2.13.47 2.14.49 2.14.50 2.15.18 2.17.21 2.17.23 2.18.17 2.19.44 2.19.45 2.19.46 2.19.49 2.2.12 2.2.15 2.2.16 2.2.18 2.2.8 2.20.31 2.20.32 2.20.33 2.21.11 2.21.12 2.21.16 2.21.25 2.22.32 2.23.13 2.23.15 2.23.16 2.23.18 2.24.12 2.24.14 2.24.18 2.25.14 2.26.6 2.28.10 2.28.13 2.28.14 2.28.7 2.29.1 2.29.2 2.29.3 2.3.0 2.3.1 2.3.2 2.3.3 2.30.18
tenweb-speed-optimizer / includes / OptimizerUtils.php
tenweb-speed-optimizer / includes Last commit date
WebPageCache 7 months ago external 2 months ago ExcludeJsFromDelay.php 2 years ago OptimizerBanner.php 11 months ago OptimizerBase.php 2 years ago OptimizerCSSMin.php 2 years ago OptimizerCache.php 1 year ago OptimizerCacheStructure.php 2 years ago OptimizerCriticalCss.php 1 year ago OptimizerElementor.php 2 years ago OptimizerFonts.php 2 years ago OptimizerImages.php 1 year ago OptimizerLogger.php 1 year ago OptimizerMain.php 2 months ago OptimizerOnInit.php 11 months ago OptimizerScripts.php 2 years ago OptimizerSettings.php 2 months ago OptimizerStyles.php 2 years ago OptimizerUrl.php 1 year ago OptimizerUtils.php 2 months ago OptimizerWhiteLabel.php 2 years ago
OptimizerUtils.php
3003 lines
1 <?php
2
3 namespace TenWebOptimizer;
4
5 use autoptimizeCache;
6 use Cache_Enabler;
7 use Endurance_Page_Cache;
8 use Exception;
9 use JSMin\JSMin;
10 use TenWebIO\CompressService;
11 use WP_Optimize_Cache_Commands;
12 use WP_Rewrite;
13 use WP_Term;
14 use WP_User;
15 use WpeCommon;
16
17 /*
18 * General helpers.
19 */
20 if (!defined('ABSPATH')) {
21 exit;
22 }
23
24 class OptimizerUtils
25 {
26 const TWO_INCOMPATIBLE_PLUGIN_LIST = [
27 'w3-total-cache/w3-total-cache.php',
28 'wp-super-cache/wp-cache.php',
29 'wp-rocket/wp-rocket.php',
30 'rocket-footer-js/rocket-footer-js.php',
31 'autoptimize/autoptimize.php',
32 'perfmatters/perfmatters.php',
33 'wp-fastest-cache/wpFastestCache.php',
34 'wp-optimize/wp-optimize.php',
35 'wp-asset-clean-up/wpacu.php',
36 'rocket-lazy-load/rocket-lazy-load.php',
37 'hummingbird-performance/wp-hummingbird.php',
38 'wp-hummingbird/wp-hummingbird.php',
39 'flying-scripts/flying-scripts.php',
40 'async-javascript/async-javascript.php',
41 'nitropack/main.php',
42 'psn-pagespeed-ninja/pagespeedninja.php',
43 'swift-performance-lite/performance.php',
44 'swift-performance/performance.php',
45 'fast-velocity-minify/fvm.php',
46 'wp-performance-score-booster/wp-performance-score-booster.php',
47 'ezoic-integration/ezoic-integration.php',
48 'a3-lazy-load/a3-lazy-load.php',
49 'page-optimize/page-optimize.php',
50 'wp-smushit/wp-smush.php',
51 'performance-lab/load.php',
52 'airlift/airlift.php',
53 ];
54
55 const OPTIMIZED_BG_MARKER = '++TWO_OPTIMIZED_BG_IMAGE++';
56
57 const SVG_DATA = 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20';
58
59 const BG_SVG_PLACEHOLDER = OptimizerUtils::SVG_DATA . '%20' . '%22%3E%3C/svg%3E' . '#}';
60
61 const BACKGROUND_IMAGE_REGEXP_NEW__PERFECT__SLOW = "~(?:\})*(.*)\{(?:[^}]|\w\s\S)*(?:background[-image]*\s*:.*url.*\(\s*((?:[\'\"]{1}(?:.*)?[\'\"]{1})|(?:[^\'\"]{1}(?:.*)?[^\'\"]{1}))\s*\))+[^}]*\}~U";
62
63 const BACKGROUND_IMAGE_REGEXP_OLD__NOT_PERFECT__FAST = "~(.*)\{(?:[^}]|\w\s\S)*(?:background[-image]*?\s*:.*?url.*?\(\s*((?:[\'|\"]{1}(?:.*?)?[\'|\"]{1})|(?:[^\'|\"]{1}(?:.*?)?[^\'|\"]{1}))\s*\))+[^}]*\}~";
64
65 const FROM_PLUGIN = '10webspeedoptimizer';
66
67 const MODES = [
68 'extreme' => [
69 'title' => 'Extreme',
70 'mode' => 'extreme',
71 'two_delay_all_js_execution' => true,
72 'critical_enabled' => true,
73 'lazy_load_type' => 'vanilla',
74 'next' => 'strong',
75 'level' => 1,
76 ],
77 'strong' => [
78 'title' => 'Strong',
79 'mode' => 'strong',
80 'two_delay_all_js_execution' => true,
81 'critical_enabled' => false,
82 'lazy_load_type' => 'vanilla',
83 'next' => 'balanced',
84 'level' => 2,
85 ],
86 'balanced' => [
87 'title' => 'Balanced',
88 'mode' => 'balanced',
89 'two_delay_all_js_execution' => false,
90 'critical_enabled' => true,
91 'lazy_load_type' => 'vanilla',
92 'next' => 'standard',
93 'level' => 3,
94 ],
95 'standard' => [
96 'title' => 'Standard',
97 'mode' => 'standard',
98 'two_delay_all_js_execution' => false,
99 'critical_enabled' => false,
100 'lazy_load_type' => 'browser',
101 'next' => 'no_optimize',
102 'level' => 4,
103 ],
104 'no_optimize' => [
105 'title' => 'No optimize',
106 'mode' => 'no_optimize',
107 'next' => '0',
108 'level' => 5,
109 ],
110 ];
111
112 private static $has_changed_bg_image = false;
113
114 /**
115 * Returns true when mbstring is available.
116 *
117 * @param bool|null $override allows overriding the decision
118 *
119 * @return bool
120 */
121 public static function mbstring_available($override = null)
122 {
123 static $available = null;
124
125 if (null === $available) {
126 $available = \extension_loaded('mbstring');
127 }
128
129 if (null !== $override) {
130 $available = $override;
131 }
132
133 return $available;
134 }
135
136 /**
137 * Multibyte-capable strpos() if support is available on the server.
138 * If not, it falls back to using \strpos().
139 *
140 * @param string $haystack haystack
141 * @param string $needle needle
142 * @param int $offset offset
143 * @param string|null $encoding Encoding. Default null.
144 *
145 * @return int|false
146 */
147 public static function strpos($haystack, $needle, $offset = 0, $encoding = null)
148 {
149 if (self::mbstring_available()) {
150 return (null === $encoding) ? \mb_strpos($haystack, $needle, $offset) : \mb_strpos($haystack, $needle, $offset, $encoding);
151 } else {
152 return \strpos($haystack, $needle, $offset);
153 }
154 }
155
156 /**
157 * Finds the position of the <body> tag in an HTML string.
158 *
159 * This method searches for the first occurrence of the <body> tag
160 * in the given HTML content. If a </head> tag is found after the <body>
161 * tag, it will search for the next <body> tag.
162 *
163 * @param string $haystack the HTML content as a string
164 *
165 * @return int|false the position of the <body> tag, or false if not found
166 */
167 public static function get_body_position($haystack)
168 {
169 // Find the first occurrence of the <body> tag
170 $bodyPosition = self::strpos($haystack, '<body');
171
172 // If <body> tag is not found, return false
173 if ($bodyPosition === false) {
174 return false;
175 }
176
177 // Extract substring starting from the <body> tag
178 $afterBody = substr($haystack, $bodyPosition);
179
180 // Check if there's a </head> tag after the found <body> tag
181 $headClosePosition = self::strpos($afterBody, '</head>');
182
183 // If </head> is found after <body>, search for the next <body> tag
184 if ($headClosePosition !== false) {
185 // Search for the next <body> tag after </head>
186 $bodyPosition = self::strpos($haystack, '<body', $bodyPosition + 1);
187 }
188
189 return $bodyPosition;
190 }
191
192 /**
193 * Multibyte-capable strrpos() if support is available on the server.
194 * If not, it falls back to using \strrpos().
195 *
196 * @param string $haystack haystack
197 * @param string $needle needle
198 * @param int $offset offset
199 * @param string|null $encoding Encoding. Default null.
200 *
201 * @return int|false
202 */
203 public static function strrpos($haystack, $needle, $offset = 0, $encoding = null)
204 {
205 if (self::mbstring_available()) {
206 return (null === $encoding) ? \mb_strrpos($haystack, $needle, $offset) : \mb_strrpos($haystack, $needle, $offset, $encoding);
207 } else {
208 return \strrpos($haystack, $needle, $offset);
209 }
210 }
211
212 /**
213 * Attempts to return the number of characters in the given $string if
214 * mbstring is available. Returns the number of bytes
215 * (instead of characters) as fallback.
216 *
217 * @param string $string string
218 * @param string|null $encoding encoding
219 *
220 * @return int number of charcters or bytes in given $string
221 * (characters if/when supported, bytes otherwise)
222 */
223 public static function strlen($string, $encoding = null)
224 {
225 if (self::mbstring_available()) {
226 return (null === $encoding) ? \mb_strlen($string) : \mb_strlen($string, $encoding);
227 } else {
228 return \strlen($string);
229 }
230 }
231
232 /**
233 * Our wrapper around implementations of \substr_replace()
234 * that attempts to not break things horribly if at all possible.
235 * Uses mbstring if available, before falling back to regular
236 * substr_replace() (which works just fine in the majority of cases).
237 *
238 * @param string $string string
239 * @param string $replacement replacement
240 * @param int $start start offset
241 * @param int|null $length length
242 * @param string|null $encoding encoding
243 *
244 * @return string
245 */
246 public static function substr_replace($string, $replacement, $start, $length = null, $encoding = null)
247 {
248 if (self::mbstring_available()) {
249 $strlen = self::strlen($string, $encoding);
250
251 if ($start < 0) {
252 if (-$start < $strlen) {
253 $start = $strlen + $start;
254 } else {
255 $start = 0;
256 }
257 } elseif ($start > $strlen) {
258 $start = $strlen;
259 }
260
261 if (null === $length || '' === $length) {
262 $start2 = $strlen;
263 } elseif ($length < 0) {
264 $start2 = $strlen + $length;
265
266 if ($start2 < $start) {
267 $start2 = $start;
268 }
269 } else {
270 $start2 = $start + $length;
271 }
272
273 if (null === $encoding) {
274 $leader = $start ? \mb_substr($string, 0, $start) : '';
275 $trailer = ($start2 < $strlen) ? \mb_substr($string, $start2, null) : '';
276 } else {
277 $leader = $start ? \mb_substr($string, 0, $start, $encoding) : '';
278 $trailer = ($start2 < $strlen) ? \mb_substr($string, $start2, null, $encoding) : '';
279 }
280
281 return "{$leader}{$replacement}{$trailer}";
282 }
283
284 return (null === $length) ? \substr_replace($string, $replacement, $start) : \substr_replace($string, $replacement, $start, $length);
285 }
286
287 /**
288 * Decides whether this is a "subdirectory site" or not.
289 *
290 * @param bool $override allows overriding the decision when needed
291 *
292 * @return bool
293 */
294 public static function siteurl_not_root($override = null)
295 {
296 static $subdir = null;
297
298 if (null === $subdir) {
299 $parts = self::get_ao_wp_site_url_parts();
300 $subdir = (isset($parts['path']) && ('/' !== $parts['path']));
301 }
302
303 if (null !== $override) {
304 $subdir = $override;
305 }
306
307 return $subdir;
308 }
309
310 /**
311 * Parse TWO_WP_SITE_URL into components using \parse_url(), but do
312 * so only once per request/lifecycle.
313 *
314 * @return array
315 */
316 public static function get_ao_wp_site_url_parts()
317 {
318 static $parts = [];
319
320 if (empty($parts)) {
321 $parts = \wp_parse_url(TWO_WP_SITE_URL);
322 }
323
324 return $parts;
325 }
326
327 /**
328 * Modify given $cdn_url to include the site path when needed.
329 *
330 * @param string $cdn_url CDN URL to tweak
331 * @param bool $force_cache_miss force a cache miss in order to be able
332 * to re-run the filter
333 *
334 * @return string
335 */
336 public static function tweak_cdn_url_if_needed($cdn_url, $force_cache_miss = false)
337 {
338 static $results = [];
339
340 if (!isset($results[$cdn_url]) || $force_cache_miss) {
341
342 // In order to return unmodified input when there's no need to tweak.
343 $results[$cdn_url] = $cdn_url;
344
345 // Behind a default true filter for backcompat, and only for sites
346 // in a subfolder/subdirectory, but still easily turned off if
347 // not wanted/needed...
348 if (OptimizerUtils::siteurl_not_root()) {
349 $site_url_parts = OptimizerUtils::get_ao_wp_site_url_parts();
350 $cdn_url_parts = \wp_parse_url($cdn_url);
351 $schemeless = self::is_protocol_relative($cdn_url);
352 $cdn_url_parts = self::maybe_replace_cdn_path($site_url_parts, $cdn_url_parts);
353
354 if (false !== $cdn_url_parts) {
355 $results[$cdn_url] = self::assemble_parsed_url($cdn_url_parts, $schemeless);
356 }
357 }
358 }
359
360 return $results[$cdn_url];
361 }
362
363 /**
364 * When siteurl contans a path other than '/' and the CDN URL does not have
365 * a path or it's path is '/', this will modify the CDN URL's path component
366 * to match that of the siteurl.
367 * This is to support "magic" CDN urls that worked that way before v2.4...
368 *
369 * @param array $site_url_parts site URL components array
370 * @param array $cdn_url_parts CDN URL components array
371 *
372 * @return array|false
373 */
374 public static function maybe_replace_cdn_path(array $site_url_parts, array $cdn_url_parts)
375 {
376 if (isset($site_url_parts['path']) && '/' !== $site_url_parts['path']) {
377 if (!isset($cdn_url_parts['path']) || '/' === $cdn_url_parts['path']) {
378 $cdn_url_parts['path'] = $site_url_parts['path'];
379
380 return $cdn_url_parts;
381 }
382 }
383
384 return false;
385 }
386
387 /**
388 * Given an array or components returned from \parse_url(), assembles back
389 * the complete URL.
390 * If optional
391 *
392 * @param array $parsed_url URL components array
393 * @param bool $schemeless whether the assembled URL should be
394 * protocol-relative (schemeless) or not
395 *
396 * @return string
397 */
398 public static function assemble_parsed_url(array $parsed_url, $schemeless = false)
399 {
400 $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
401
402 if ($schemeless) {
403 $scheme = '//';
404 }
405 $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
406 $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
407 $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
408 $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
409 $pass = ($user || $pass) ? "$pass@" : '';
410 $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
411 $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
412 $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
413
414 return "$scheme$user$pass$host$port$path$query$fragment";
415 }
416
417 /**
418 * Returns true if given $url is protocol-relative.
419 *
420 * @param string $url URL to check
421 *
422 * @return bool
423 */
424 public static function is_protocol_relative($url)
425 {
426 $result = false;
427
428 if (!empty($url)) {
429 $result = (0 === strpos($url, '//'));
430 }
431
432 return $result;
433 }
434
435 /**
436 * Canonicalizes the given path regardless of it existing or not.
437 *
438 * @param string $path path to normalize
439 *
440 * @return string
441 */
442 public static function path_canonicalize($path)
443 {
444 $patterns = [
445 '~/{2,}~',
446 '~/(\./)+~',
447 '~([^/\.]+/(?R)*\.{2,}/)~',
448 '~\.\./~',
449 ];
450 $replacements = [
451 '/',
452 '/',
453 '',
454 '',
455 ];
456
457 return preg_replace($patterns, $replacements, $path);
458 }
459
460 /**
461 * Returns true if the string is a valid regex.
462 *
463 * @param string $string string, duh
464 *
465 * @return bool
466 */
467 public static function str_is_valid_regex($string)
468 {
469 set_error_handler(function () {}, E_WARNING); // phpcs:ignore
470 $is_regex = (false !== preg_match($string, ''));
471 restore_error_handler();
472
473 return $is_regex;
474 }
475
476 /**
477 * Returns true if a certain WP plugin is active/loaded.
478 *
479 * @param string $plugin_file main plugin file
480 *
481 * @return bool
482 */
483 public static function is_plugin_active($plugin_file)
484 {
485 static $ipa_exists = null;
486
487 if (null === $ipa_exists) {
488 if (!function_exists('\is_plugin_active')) {
489 require_once ABSPATH . 'wp-admin/includes/plugin.php';
490 }
491 $ipa_exists = function_exists('\is_plugin_active');
492 }
493
494 return $ipa_exists && \is_plugin_active($plugin_file);
495 }
496
497 public static function replace_font($css)
498 {
499 global $TwoSettings;
500 $two_async_font = $TwoSettings->get_settings('two_async_font');
501
502 if (isset($two_async_font) && $two_async_font == 'on') {
503 /*$re = '~(?>@font-face\s*{\s*|\G(?!\A))(\S+)\s*:\s*valet([^;]+);\s*~';*/ // phpcs:ignore
504 $re = '/@font-face.*{\K[^}]*(?=})/';
505 preg_match_all($re, $css, $matches, PREG_SET_ORDER, 0);
506
507 foreach ($matches as $el) {
508 if (isset($el[0])) {
509 if (strpos($el[0], 'font-display') !== false) {
510 $re1 = '/font-display\s*:\s*\K[^;]*(?=;)/';
511 preg_match_all($re1, $el[0], $elMatches, PREG_SET_ORDER, 0);
512
513 if (isset($elMatches, $elMatches[0], $elMatches[0][0])) {
514 $style = str_replace($elMatches[0][0], 'swap;', $el[0]);
515 $css = str_replace($el[0], $style, $css);
516 }
517 } else {
518 $style = $el[0] . ';font-display: swap;';
519 $css = str_replace($el[0], $style, $css);
520 }
521 }
522 }
523 }
524
525 return $css;
526 }
527
528 public static function replace_google_font_url($url)
529 {
530 if (strpos($url, 'display')) {
531 $url = str_replace('&amp;', '&', $url);
532 $parsed_url = wp_parse_url(urldecode($url));
533 parse_str($parsed_url['query'], $url_params);
534
535 if (isset($url_params['display'])) {
536 $url = str_replace($url_params['display'], 'swap', $url);
537 } else {
538 $url = add_query_arg('display', 'swap', $url);
539 }
540 } else {
541 $url = add_query_arg('display', 'swap', $url);
542 }
543
544 return $url;
545 }
546
547 public static function serve_different_sizes_for_critical_bg_image($images_data)
548 {
549 //tenweb_optimizer_mobile
550 $imagesArray = [];
551 $css = '';
552
553 if (is_array($images_data)) {
554 $allSizes = get_intermediate_image_sizes(); // phpcs:ignore
555
556 foreach ($images_data as $image_data) {
557 if (isset($image_data[ 'bg_url' ]) && isset($image_data[ 'selector' ]) && is_array($image_data[ 'selector' ])) {
558 $imageId = self::getImageIdByUrl(strtok($image_data[ 'bg_url' ], '?'));
559
560 if ($imageId) {
561 $css_rule = implode(':not(.two_bg), ', $image_data['selector']);
562 $css_rule .= ':not(.two_bg)';
563
564 //create an array with all image sizes
565 foreach ($allSizes as $i => $size) {
566 $imagesArray[ $size ] = wp_get_attachment_image_src($imageId, $size);
567 }
568 $important = '';
569
570 if (isset($image_data[ 'value' ]) && strpos($image_data[ 'value' ], '!important') < 1) {
571 $important = ' !important; ';
572 }
573 $mobileRule = '';
574
575 foreach ($imagesArray as $size => $imageArray) {
576 if (!empty($imageArray)) {
577 if ($size === 'medium_large' && empty($mobileRule)) {
578 if (isset($imageArray[ 0 ]) && !empty($imageArray[ 0 ])) {
579 if (isset($image_data[ 'value' ]) && strpos($image_data[ 'value' ], $image_data[ 'bg_url' ]) !== false) {
580 $css_param_value = str_replace($image_data[ 'bg_url' ], $imageArray[ 0 ], $image_data[ 'value' ]);
581 $mobileRule = ' background-image: ' . $css_param_value . $important;
582 } else {
583 $mobileRule = ' background-image: url(' . $imageArray[ 0 ] . ') !important; ';
584 }
585 }
586 break;
587 }
588 }
589 }
590
591 //fallback to elementor images
592 if (empty($mobileRule)) {
593 if (isset($imagesArray[ 'tenweb_optimizer_mobile' ])) {
594 if (isset($imagesArray[ 'tenweb_optimizer_mobile' ][ 0 ]) && !empty($imagesArray[ 'tenweb_optimizer_mobile' ][ 0 ])) {
595 if (isset($image_data[ 'value' ]) && strpos($image_data[ 'value' ], $image_data[ 'bg_url' ]) !== false) {
596 $css_param_value = str_replace($image_data[ 'bg_url' ], $imagesArray[ 'tenweb_optimizer_mobile' ][ 0 ], $image_data[ 'value' ]);
597 $mobileRule = ' background-image: ' . $css_param_value . $important;
598 } else {
599 $mobileRule = ' background-image: url(' . $imagesArray[ 'tenweb_optimizer_mobile' ][ 0 ] . ') !important; ';
600 }
601 }
602 }
603 }
604
605 //generate media css blocks and add to the end of css file
606 if (!empty($mobileRule)) {
607 $mobileCss = "\r\n" . rtrim($css_rule, ',')
608 . ' { ' . $mobileRule . ' } ';
609 $css .= ' ' . $mobileCss;
610 }
611 }
612 }
613 }
614 }
615
616 if ($css) {
617 $css = self::replace_bg($css);
618 $css = "/* Autogenerated by 10Web Booster plugin*/\r\n
619 @media (min-width: 320px) and (max-width: 480px) { \r\n" . $css . '}';
620 }
621
622 return $css;
623 }
624
625 public static function getImageIdByUrl($url)
626 {
627 global $wpdb;
628 // If the URL is auto-generated thumbnail, remove the sizes and get the URL of the original image
629 $url = preg_replace('/-\d+x\d+(?=\.(jpg|jpeg|png|gif)$)/i', '', $url);
630 // phpcs:ignore Squiz.PHP.CommentedOutCode.Found
631 // $imgid = attachment_url_to_postid($url); // phpcs:ignore
632 $wp_uploads = wp_upload_dir();
633 $attachment_id = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s LIMIT 1", str_replace($wp_uploads['baseurl'] . '/', '', $url))); // phpcs:ignore
634
635 return $attachment_id;
636 }
637
638 /**
639 * This is the copy of WP function to regenerate metadata if it is set but is missing sizes for some reason.
640 * The original is in wp-includes/media.php
641 *
642 * Maybe attempts to generate attachment metadata, if missing.
643 *
644 * @param WP_Post $attachment attachment object
645 */
646 public static function wp_maybe_generate_attachment_metadata($attachment)
647 {
648 if (empty($attachment) || empty($attachment->ID)) {
649 return;
650 }
651
652 $attachment_id = (int) $attachment->ID;
653 $file = get_attached_file($attachment_id);
654 $meta = wp_get_attachment_metadata($attachment_id);
655
656 if (empty($meta) && file_exists($file)) {
657 $_meta = get_post_meta($attachment_id);
658 $_lock = 'wp_generating_att_' . $attachment_id;
659
660 if ((! array_key_exists('_wp_attachment_metadata', $_meta) || empty($_meta[ '_wp_attachment_metadata' ][ 'sizes' ])) && ! \TenWebWpTransients\OptimizerTransients::get($_lock)) {
661 \TenWebWpTransients\OptimizerTransients::set($_lock, $file);
662 wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata($attachment_id, $file));
663 \TenWebWpTransients\OptimizerTransients::delete($_lock);
664 }
665 }
666 }
667
668 public static function replace_bg($css)
669 {
670 /* Exclude ::after and ::before elements from CSS because if we change their bg urls,
671 we cannot restore it on the fronted using JS */
672 $css_without_after_before = $css;
673
674 if (preg_match_all('#(::before.*})|(::after.*})#Usmi', $css_without_after_before, $css_matches)) {
675 foreach ($css_matches[0] as $css_block) {
676 $css_without_after_before = str_replace($css_block, '}', $css_without_after_before);
677 }
678 }
679
680 $replaced_images = [];
681 global $TwoSettings;
682 $two_lazyload = $TwoSettings->get_settings('two_lazyload');
683 $two_bg_lazyload = $TwoSettings->get_settings('two_bg_lazyload');
684 $two_img_in_viewport_lazyload = $TwoSettings->get_settings('two_img_in_viewport_lazyload');
685 $critical = new OptimizerCriticalCss();
686
687 if (TWO_LAZYLOAD && isset($two_bg_lazyload) && ($two_bg_lazyload == 'on' || ($two_img_in_viewport_lazyload == 'on' && $critical->images_in_viewport)) && !$critical->use_uncritical) {
688 // phpcs:ignore Squiz.PHP.CommentedOutCode.Found
689 //$re = '~\bbackground[-image]*?\s*:.*?url.*?\(\s*[\'|"]?(.*?)?[\'|"]?\s*\)~i'; // phpcs:ignore
690 //$re = '~url\s*\(\s*[\'|"]?(.*?)?[\'|"]?\s*\)~i'; // phpcs:ignore
691 //$re = '~\bbackground[-image]*?\s*:.*?url.*?\(\s*[\'|"]?(.*?)?[\'|"]\s*\)~i'; // phpcs:ignore
692 $re = '~\bbackground[-image]*?\s*:.*?url.*?\(\s*?(.*?)?\s*\)~i';
693 preg_match_all($re, $css_without_after_before, $matches);
694 $ext_list = ['svg', 'jpeg', 'png', 'gif', 'jpg', 'webp', 'bmp'];
695
696 if (isset($matches[1]) && is_array($matches)) {
697 $images_urls = $matches[1];
698 $bg_styles = $matches[0];
699
700 foreach ($images_urls as $key => $src) {
701 $src = htmlspecialchars_decode($src, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401);
702 $src = str_replace(["'", '"'], [''], $src);
703 $url = strtok($src, '?');
704 $ext = pathinfo($url, PATHINFO_EXTENSION);
705 $current_bg_style = '';
706
707 if (is_array($bg_styles) && isset($bg_styles[$key])) {
708 $current_bg_style = $bg_styles[$key];
709 }
710
711 if (!in_array(strtolower($ext), $ext_list)) {
712 continue;
713 }
714
715 if (in_array($src, $replaced_images)) {
716 continue;
717 }
718
719 //pass serve_different_sizes_for_bg_image added backgrounds
720 if (self::strpos($src, self::OPTIMIZED_BG_MARKER) !== false) {
721 continue;
722 }
723 $flag_continue = false;
724 $two_exclude_lazyload = $TwoSettings->get_settings('two_exclude_lazyload');
725
726 if (isset($two_exclude_lazyload) && !empty($two_exclude_lazyload)) {
727 $exclude_lazyload = explode(',', $two_exclude_lazyload);
728
729 foreach ($exclude_lazyload as $path) {
730 if (strpos($src, $path) !== false) {
731 $flag_continue = true;
732 }
733 }
734 }
735
736 if ($flag_continue === false && $two_img_in_viewport_lazyload == 'on' && $critical->images_in_viewport) {
737 $abs_src = self::get_absolute_url($src);
738 $flag_continue = in_array($abs_src, $critical->images_in_viewport);
739 }
740
741 if ($flag_continue) {
742 continue;
743 }
744 $replaced_images[] = $src;
745
746 $pos = strpos($src, '#}');
747
748 if ($pos === false && !empty($current_bg_style)) {
749 $new_bg_style = str_replace($src, self::BG_SVG_PLACEHOLDER . $src, $current_bg_style);
750 $css = str_replace($current_bg_style, $new_bg_style, $css);
751 } elseif ($pos === false) {
752 $css = str_replace($src, self::BG_SVG_PLACEHOLDER . $src, $css);
753 }
754 self::$has_changed_bg_image = true;
755 }
756 }
757 }
758
759 return $css;
760 }
761
762 /**
763 * Cache compare.
764 *
765 * @param array $args
766 */
767 public static function cache_compare($args = [])
768 {
769 $type = $args['type'];
770 $post_id = $args['post_id'];
771 $new_cache_files = $args['new_cache_files'];
772 $old_cache_files = get_post_meta($post_id, 'two_cache_' . $type, true);
773
774 if (!empty($new_cache_files) && empty($old_cache_files)) {
775 add_post_meta($post_id, 'two_cache_' . $type, []);
776 }
777
778 if (!empty($new_cache_files)) {
779 update_post_meta($post_id, 'two_cache_' . $type, $new_cache_files);
780
781 if (!empty($old_cache_files)) {
782 $dir_gzip = OptimizerCache::get_path();
783
784 foreach ($old_cache_files as $old_file) {
785 if (!in_array($old_file, $new_cache_files)) {
786 $old_file_name = ($type == 'gzip') ? $old_file : $type . '/' . $old_file;
787 self::delete_cache_file($old_file_name);
788
789 if ($type == 'gzip') {
790 foreach (['deflate', 'none', 'gzip'] as $val) {
791 $file_gzip = $old_file . '.' . $val;
792
793 if (is_file($dir_gzip . $file_gzip)) {
794 self::delete_cache_file($file_gzip);
795 }
796 }
797 }
798 }
799 }
800 }
801 }
802 }
803
804 /**
805 * Cache files parsing array.
806 *
807 * @return array
808 */
809 public static function cache_files_parsing_array()
810 {
811 $js = [];
812 $css = [];
813 $gzip = [];
814 $file_name = '_all_cache_files.txt';
815
816 if (is_file(TWO_CACHE_DIR . $file_name)) {
817 $files = file_get_contents(TWO_CACHE_DIR . $file_name); // phpcs:ignore
818
819 if (!empty($files)) {
820 OptimizerUtils::delete_cache_file($file_name);
821 $files = json_decode($files);
822
823 foreach ($files as $file) {
824 preg_match('/^css\/(.*).css$/', $file, $matches_css);
825 preg_match('/^js\/(.*).js$/', $file, $matches_js);
826 preg_match('/^(.*).php$/', $file, $matches_php);
827
828 if (!empty($matches_css)) {
829 $css[] = str_replace('css/', '', $matches_css[0]);
830 }
831
832 if (!empty($matches_js)) {
833 if (strpos($matches_js[0], 'two_snippet_') < -1) {
834 $js[] = str_replace('js/', '', $matches_js[0]);
835 }
836 }
837
838 if (!empty($matches_php)) {
839 if (strpos($matches_php[0], 'two_snippet_') < -1) {
840 $gzip[] = $matches_php[0];
841 }
842 }
843 }
844 }
845 }
846
847 return ['js' => array_unique($js), 'css' => array_unique($css), 'gzip' => array_unique($gzip)];
848 }
849
850 /**
851 * Delete all cache on DB.
852 *
853 * @param array $args
854 *
855 * @return bool
856 */
857 public static function delete_all_cache_db($args = [])
858 {
859 global $wpdb;
860 $tbl = $wpdb->prefix . 'postmeta';
861 $css = $wpdb->delete($tbl, ['meta_key' => 'two_cache_css']); // phpcs:ignore
862 $js = $wpdb->delete($tbl, ['meta_key' => 'two_cache_js']); // phpcs:ignore
863 $gzip = $wpdb->delete($tbl, ['meta_key' => 'two_cache_gzip']); // phpcs:ignore
864
865 return true;
866 }
867
868 /**
869 * Deleted recursively directory and its entire contents.
870 *
871 * @param string $dir
872 * @param array $not_allow_delete
873 *
874 * @return mixed
875 */
876 public static function delete_all_cache_file($dir = '', $not_allow_delete = [], $not_allow_folder = null)
877 {
878 if (is_dir($dir)) {
879 $objects = scandir($dir);
880
881 if (!is_array($objects)) {
882 return false;
883 }
884
885 foreach ($objects as $key => $object) {
886 if ($object === $not_allow_folder) {
887 continue;
888 }
889
890 if ($object != '.' && $object != '..') {
891 if (is_dir($dir . '/' . $object) && !is_link($dir . '/' . $object)) {
892 self::delete_all_cache_file(rtrim($dir, '/') . '/' . $object, $not_allow_delete);
893 } else {
894 unlink($dir . '/' . $object); // phpcs:ignore
895 }
896 unset($objects[$key]);
897 }
898 }
899
900 if (count($objects) === 2 && (empty($not_allow_delete) || !in_array($dir, $not_allow_delete) || empty($not_allow_folder))) {
901 rmdir($dir); // phpcs:ignore
902 }
903
904 return true;
905 }
906
907 return true;
908 }
909
910 public static function delete_files_by_prefix($prefix)
911 {
912 $dir = OptimizerCache::get_path();
913 $mask = $dir . $prefix;
914 array_map('unlink', glob($mask));
915 }
916
917 /**
918 * Delete cache file.
919 *
920 * @param string $file
921 *
922 * @return bool
923 */
924 public static function delete_cache_file($file = '')
925 {
926 $file = OptimizerCache::get_path() . $file;
927
928 if (is_file($file)) {
929 $delete = @unlink($file); // phpcs:ignore
930
931 if ($delete) {
932 return true;
933 }
934 }
935
936 return false;
937 }
938
939 public static function get_page_url()
940 {
941 if (isset($_SERVER['HTTP_HOST']) && isset($_SERVER['REQUEST_URI'])) {
942 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
943 $link = 'https';
944 } else {
945 $link = 'http';
946 }
947 // Here append the common URL characters.
948 $link .= '://';
949 // Append the host(domain name, ip) to the URL.
950 $link .= sanitize_text_field($_SERVER['HTTP_HOST']);
951 // Append the requested resource location to the URL
952 $link .= sanitize_text_field($_SERVER['REQUEST_URI']);
953
954 return $link;
955 }
956
957 return '';
958 }
959
960 public static function is_pagespeed_enabled()
961 {
962 return defined('TW_NGX_PAGESPEED') && TW_NGX_PAGESPEED === 'enabled';
963 }
964
965 public static function is_pagespeed_lazyload_enabled()
966 {
967 return self::is_pagespeed_enabled() && defined('TW_NGX_PAGESPEED_FILTERS') && in_array('lazyload_images', TW_NGX_PAGESPEED_FILTERS, true);
968 }
969
970 public static function is_pagespeed_image_optimization_enables()
971 {
972 return self::is_pagespeed_enabled() && defined('TW_NGX_PAGESPEED_FILTERS') &&
973 (
974 in_array('convert_gif_to_png', TW_NGX_PAGESPEED_FILTERS, true) ||
975 in_array('recompress_png', TW_NGX_PAGESPEED_FILTERS, true) ||
976 in_array('convert_png_to_jpeg', TW_NGX_PAGESPEED_FILTERS, true) ||
977 in_array('convert_jpeg_to_progressive', TW_NGX_PAGESPEED_FILTERS, true) ||
978 in_array('recompress_jpeg', TW_NGX_PAGESPEED_FILTERS, true) ||
979 in_array('convert_jpeg_to_webp', TW_NGX_PAGESPEED_FILTERS, true) ||
980 in_array('convert_to_webp_lossless', TW_NGX_PAGESPEED_FILTERS, true) ||
981 in_array('convert_to_webp_animated', TW_NGX_PAGESPEED_FILTERS, true) ||
982 in_array('recompress_webp', TW_NGX_PAGESPEED_FILTERS, true) ||
983 in_array('inline_images', TW_NGX_PAGESPEED_FILTERS, true) ||
984 in_array('resize_images', TW_NGX_PAGESPEED_FILTERS, true)
985 );
986 }
987
988 public static function is_pagespeed_js_defer_enabled()
989 {
990 return self::is_pagespeed_enabled() && defined('TW_NGX_PAGESPEED_FILTERS') && in_array('defer_javascript', TW_NGX_PAGESPEED_FILTERS, true);
991 }
992
993 public static function purge_pagespeed_cache()
994 {
995 if (OptimizerUtils::is_pagespeed_enabled()) {
996 $url = rtrim(get_home_url(), '/') . '/*';
997 $response = wp_remote_request($url, ['method' => 'PURGE']);
998 }
999
1000 return true;
1001 }
1002
1003 /**
1004 * remove markers that serve_different_sizes_for_bg_image added for backgrounds
1005 *
1006 * @return string|string[]
1007 */
1008 public static function removeBgImageMarkers($css)
1009 {
1010 return str_replace(
1011 self::OPTIMIZED_BG_MARKER,
1012 '',
1013 str_replace(
1014 self::OPTIMIZED_BG_MARKER . self::SVG_DATA . '#}',
1015 '',
1016 $css
1017 )
1018 );
1019 }
1020
1021 /**
1022 * Run a match on the array's keys
1023 *
1024 * @param int $flags
1025 *
1026 * @return array
1027 */
1028 public static function preg_grep_keys($pattern, $input, $flags = 0)
1029 {
1030 return array_intersect_key($input, array_flip(preg_grep($pattern, array_keys($input), $flags)));
1031 }
1032
1033 /**
1034 * Checks if the current request is a WP REST API request.
1035 *
1036 * Case #1: After WP_REST_Request initialisation
1037 * Case #2: Support "plain" permalink settings
1038 * Case #3: It can happen that WP_Rewrite is not yet initialized,
1039 * so do this (wp-settings.php)
1040 * Case #4: URL Path begins with wp-json/ (your REST prefix)
1041 * Also supports WP installations in subfolders
1042 *
1043 * @returns boolean
1044 */
1045 public static function is_rest()
1046 {
1047 if ((defined('REST_REQUEST') && REST_REQUEST)
1048 || (isset($_GET['rest_route']))) { // phpcs:ignore
1049 return true;
1050 }
1051 global $wp_rewrite;
1052
1053 if ($wp_rewrite === null) {
1054 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1055 $wp_rewrite = new WP_Rewrite();
1056 }
1057
1058 $rest_url = wp_parse_url(trailingslashit(rest_url()));
1059 $current_url = wp_parse_url(add_query_arg([]));
1060
1061 return isset($current_url['path']) && strpos($current_url['path'], $rest_url['path'], 0) === 0;
1062 }
1063
1064 /**
1065 * Get parameters from a URL string
1066 *
1067 * @return bool|mixed
1068 */
1069 public static function get_url_query($url, $name)
1070 {
1071 $url_params = wp_parse_url($url);
1072
1073 if (is_array($url_params) && isset($url_params['query'])) {
1074 parse_str($url_params['query'], $query_array);
1075
1076 if (is_array($query_array) && isset($query_array[$name])) {
1077 $url_param = $query_array[$name];
1078
1079 return $url_param;
1080 }
1081 }
1082
1083 return false;
1084 }
1085
1086 /**
1087 * Remove domain part of a url
1088 *
1089 * @return string
1090 */
1091 public static function remove_domain_part($url)
1092 {
1093 $urlparts = wp_parse_url($url);
1094 $extracted = '';
1095
1096 if (isset($urlparts['path'])) {
1097 $extracted = $urlparts['path'];
1098 }
1099
1100 if (isset($urlparts['query'])) {
1101 $extracted .= '?' . $urlparts['query'];
1102 }
1103
1104 return $extracted;
1105 }
1106
1107 public static function get_javascipt_type($tag)
1108 {
1109 preg_match('/type="(.+?)"/', $tag, $matches);
1110
1111 return isset($matches[1]) ? $matches[1] : 'text/javascript';
1112 }
1113
1114 /**
1115 * Injects/replaces the given payload markup into `$this->content`
1116 * at the specified location.
1117 * If the specified tag cannot be found, the payload is appended into
1118 * $this->content along with a warning wrapped inside <!--noptimize--> tags.
1119 *
1120 * @param string $payload markup to inject
1121 * @param array $where Array specifying the tag name and method of injection.
1122 * Index 0 is the tag name (i.e., `</body>`).
1123 * Index 1 specifies ˛'before', 'after' or 'replace'. Defaults to 'before'.
1124 *
1125 * @return string
1126 */
1127 public static function inject_in_html($content, $payload, $where)
1128 {
1129
1130 if ($where[0] === '</body>') {
1131 $position_function = 'strrpos'; //choose the latest matching element
1132 } else {
1133 $position_function = 'strpos'; //choose the first matching element
1134 }
1135
1136 if ($where[0] === '<body') {
1137 $position = self::get_body_position($content);
1138 } else {
1139 $position = self::$position_function($content, $where[0]);
1140 }
1141
1142 if (false !== $position) {
1143 // Found the tag, setup content/injection as specified.
1144 if ('after' === $where[1]) {
1145 $replacement = $where[0] . $payload;
1146 } elseif ('replace' === $where[1]) {
1147 $replacement = $payload;
1148 } elseif ('after_tag' === $where[1]) {
1149 $afterTag = mb_substr($content, $position);
1150 $tagEndPosition = self::$position_function($afterTag, '>');
1151 $position = $position + $tagEndPosition;
1152 $replacement = '>' . $payload;
1153 } else {
1154 $replacement = $payload . $where[0];
1155 }
1156 $replacementLength = strlen($where[0]);
1157
1158 if ('after_tag' === $where[1]) {
1159 //count length of '>'
1160 $replacementLength = 1;
1161 }
1162 // Place where specified.
1163 $content = self::substr_replace($content, $replacement, $position, // Using plain strlen() should be safe here for now, since
1164 // we're not searching for multibyte chars here still...
1165 $replacementLength);
1166 } else {
1167 // Couldn't find what was specified, just append and add a warning.
1168 $content .= $payload;
1169 }
1170
1171 return $content;
1172 }
1173
1174 public static function isJson($string)
1175 {
1176 return is_string($string) && (is_object(json_decode($string)) || is_array(json_decode($string)));
1177 }
1178
1179 public static function findArr($arr, $field, $value)
1180 {
1181 foreach ($arr as $key => $inner_arr) {
1182 if ($inner_arr[$field] === $value) {
1183 return $arr[$key];
1184 }
1185 }
1186
1187 return false;
1188 }
1189
1190 public static function get_worker_script()
1191 {
1192 global $TwoSettings;
1193 $critical = new OptimizerCriticalCss();
1194 $merge_gf = '';
1195
1196 if ($TwoSettings->get_settings('two_merge_google_font_faces') === 'on') {
1197 $merge_gf = '<script ' . esc_attr(OptimizerScripts::TWO_DISABLE_PAGESPEED_DEFER_ATTRIBUTE) . ' ' . OptimizerScripts::TWO_NO_DELAYED_JS_ATTRIBUTE . ' type="text/javascript">
1198 ' . trim(JSMin::minify(file_get_contents(TENWEB_SO_PLUGIN_DIR . 'includes/external/js/two_merge_google_font_faces.js'))) . '
1199 </script>';
1200 }
1201 $two_font_actions = $TwoSettings->get_settings('two_font_actions');
1202
1203 return '
1204 <script ' . esc_attr(OptimizerScripts::TWO_DISABLE_PAGESPEED_DEFER_ATTRIBUTE) . ' ' . OptimizerScripts::TWO_NO_DELAYED_JS_ATTRIBUTE . ' type="text/javascript">
1205
1206 </script>' . $merge_gf . '
1207 <script ' . esc_attr(OptimizerScripts::TWO_DISABLE_PAGESPEED_DEFER_ATTRIBUTE) . ' ' . OptimizerScripts::TWO_NO_DELAYED_JS_ATTRIBUTE . ' id="two_worker" type="javascript/worker">
1208 let two_font_actions = "' . $two_font_actions . '";
1209 ' . trim(JSMin::minify(file_get_contents(TENWEB_SO_PLUGIN_DIR . 'includes/external/js/two_worker.js'))) . '
1210 </script>
1211 <script ' . esc_attr(OptimizerScripts::TWO_DISABLE_PAGESPEED_DEFER_ATTRIBUTE) . ' ' . OptimizerScripts::TWO_NO_DELAYED_JS_ATTRIBUTE . ' type="text/javascript">
1212 let two_font_actions = "' . $two_font_actions . '";
1213 ' . trim(JSMin::minify(file_get_contents(TENWEB_SO_PLUGIN_DIR . 'includes/external/js/two_delay.js'))) . '
1214 </script>';
1215 }
1216
1217 public static function clear_iframe_src($content)
1218 {
1219 if (preg_match_all('#<iframe[^>]*src[^>]*>#Usmi', $content, $matches)) {
1220 // only used is image optimization is NOT active but lazyload is.
1221 foreach ($matches[0] as $tag) {
1222 $new_tag = str_replace(' src=', ' src="" data-two_src=', $tag);
1223 $content = str_replace($tag, $new_tag, $content);
1224 }
1225 }
1226 $tags_to_remove = [
1227 [
1228 'tag' => 'link',
1229 'attribute' => 'media',
1230 'value' => 'print',
1231 ],
1232 [
1233 'tag' => 'style',
1234 'attribute' => 'media',
1235 'value' => 'print',
1236 ],
1237 [
1238 'tag' => 'script',
1239 'attribute' => 'src',
1240 'value' => 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
1241 ],
1242 ];
1243 $regex_to_remove_tags = [];
1244
1245 foreach ($tags_to_remove as $tag) {
1246 $regex_to_remove_tags[] = '(<' . $tag[ 'tag' ] . '[^>]*' . $tag[ 'attribute' ] . '=[\'"]' . $tag[ 'value' ] . '[\'"].*\/(' . $tag[ 'tag' ] . ')?>)';
1247 }
1248
1249 if (preg_match_all('#' . implode('|', $regex_to_remove_tags) . '#Usmi', $content, $matches)) {
1250 foreach ($matches[0] as $tag) {
1251 $content = str_replace($tag, '', $content);
1252 }
1253 }
1254 $content = OptimizerUtils::inject_in_html($content, '<script ' . esc_attr(OptimizerScripts::TWO_DISABLE_PAGESPEED_DEFER_ATTRIBUTE) . ' ' . esc_attr(OptimizerScripts::TWO_NO_DELAYED_JS_ATTRIBUTE) . ' src="' . plugins_url('external/js/two_elementor_video_to_iframe.js', __FILE__) . '"></script>', ['</body>', 'before']);
1255 /*this code to remove all iframes */
1256 $content = OptimizerUtils::inject_in_html($content, '<script ' . esc_attr(OptimizerScripts::TWO_DISABLE_PAGESPEED_DEFER_ATTRIBUTE) . ' ' . esc_attr(OptimizerScripts::TWO_NO_DELAYED_JS_ATTRIBUTE) . '>
1257 const two_frames = window.frames;
1258 for (let i = 0; i < two_frames.length; i++) {
1259 two_frames[i].stop();
1260 }
1261 let clear_iframe_interval = setInterval(function(){
1262 const two_dom_frames = window.frames;
1263 for (let i = 0; i < two_dom_frames.length; i++) {
1264 two_dom_frames[i].stop();
1265 }
1266 },20);
1267 setTimeout(function(){
1268 clearInterval(clear_iframe_interval);
1269 },2000);
1270
1271 </script>', ['</body>', 'before']);
1272
1273 return $content;
1274 }
1275
1276 public static function split_css_to_arr($code)
1277 {
1278 $return_data = [];
1279 $return_data['font'] = $code;
1280 preg_match_all('#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $code, $match);
1281
1282 if (isset($match[0]) && !empty($match[0])) {
1283 $return_data['urls'] = $match[0];
1284 }
1285
1286 return $return_data;
1287 }
1288
1289 public static function get_default_critical_pages($status = false)
1290 {
1291 // This theme works better without critical. There is a CLS with uncritical loaded.
1292 $theme = wp_get_theme();
1293
1294 if ('Twenty Twenty-Two' == $theme->name) {
1295 $criticalPages = [];
1296 } else {
1297 global $TwoSettings;
1298
1299 $waitUntil = 'load';
1300 $loadType = 'load_type';
1301 $use_uncritical = 'off';
1302 $two_critical_default_settings = get_option('two_critical_default_settings');
1303
1304 if (isset($two_critical_default_settings['wait_until'])) {
1305 $waitUntil = $two_critical_default_settings['wait_until'];
1306 }
1307
1308 if (isset($two_critical_default_settings['load_type'])) {
1309 $loadType = $two_critical_default_settings['load_type'];
1310 }
1311
1312 if (isset($two_critical_default_settings['use_uncritical'])) {
1313 $use_uncritical = $two_critical_default_settings['use_uncritical'];
1314 }
1315 $page_sizes = OptimizerUtils::get_critical_default_sizes($two_critical_default_settings);
1316
1317 $homeUrl = get_home_url();
1318 $pageId = 'front_page';
1319 $criticalPages = [
1320 'front_page' => [
1321 'title' => 'Home',
1322 'url' => $homeUrl,
1323 'id' => $pageId,
1324 'sizes' => $page_sizes,
1325 'load_type' => $loadType,
1326 'wait_until' => $waitUntil,
1327 'use_uncritical' => $use_uncritical
1328 ]
1329 ];
1330
1331 if ($status) {
1332 $criticalPages['front_page']['status'] = 'in_progress';
1333 }
1334 }
1335
1336 return $criticalPages;
1337 }
1338
1339 /**
1340 * @param $regeneration_mode string 'front_page' will generate for front page only
1341 * @param bool $rightAfterConnect
1342 *
1343 * @return void
1344 */
1345 public static function regenerate_critical($regeneration_mode = 'front_page', $rightAfterConnect = false)
1346 {
1347 global $TwoSettings;
1348 //todo new_flow_process get and send flow_id
1349
1350 $two_critical_pages = OptimizerUtils::getCriticalPages();
1351 $url_query = $TwoSettings->get_settings('two_critical_url_args');
1352
1353 if (empty($two_critical_pages)) {
1354 $homeUrl = get_home_url();
1355 $pageId = 'front_page';
1356
1357 $waitUntil = 'load';
1358 $two_critical_default_settings = get_option('two_critical_default_settings');
1359
1360 if (isset($two_critical_default_settings['wait_until'])) {
1361 $waitUntil = $two_critical_default_settings['wait_until'];
1362 }
1363
1364 $page_sizes = OptimizerUtils::get_critical_default_sizes($two_critical_default_settings);
1365
1366 $data = [
1367 'action' => 'two_critical',
1368 'data' => [
1369 'page_url' => $homeUrl,
1370 'page_id' => $pageId,
1371 'page_sizes' => $page_sizes,
1372 'wait_until' => $waitUntil,
1373 'url_query' => $TwoSettings->get_settings('two_critical_url_args'),
1374 'task' => 'generate',
1375 'newly_connected_website' => $rightAfterConnect,
1376 ],
1377 'two_critical_sizes' => $TwoSettings->get_settings('two_critical_sizes'),
1378 'two_critical_pages' => self::get_default_critical_pages(true),
1379 ];
1380
1381 if ($rightAfterConnect) {
1382 $data['data']['flow_id'] = get_site_option(TENWEB_PREFIX . '_flow_id');
1383 }
1384 OptimizerCriticalCss::generateCriticalCSS($data);
1385
1386 if (OptimizerUtils::is_wpml_active()) {
1387 OptimizerUtils::generate_wpml_home_pages_critical_css($data);
1388 }
1389 } else {
1390 $regenerate_data = \TenWebWpTransients\OptimizerTransients::get('two_regenerate_critical_data');
1391
1392 if (is_array($regenerate_data) && !empty($regenerate_data)) {
1393 $two_critical_pages = $regenerate_data;
1394 } else {
1395 if ('front_page' == $regeneration_mode) {
1396 // Invalidate all critical css.
1397 self::update_critical_statuses($two_critical_pages, 'not_started');
1398
1399 // Regenerate front page only.
1400 foreach ($two_critical_pages as $key => $two_page) {
1401 if ('front_page' != $key) {
1402 unset($two_critical_pages[$key]);
1403 }
1404 }
1405 }
1406 self::update_critical_statuses($two_critical_pages, 'in_progress');
1407 }
1408 $two_critical_sizes = $TwoSettings->get_settings('two_critical_sizes');
1409
1410 if (!empty($two_critical_pages)) {
1411 $two_page = reset($two_critical_pages);
1412 $key = key($two_critical_pages);
1413 unset($two_critical_pages[$key]);
1414 $critical_sizes = [];
1415
1416 if (isset($two_page['sizes']) && is_array($two_page['sizes'])) {
1417 foreach ($two_page['sizes'] as $size_id) {
1418 if (isset($size_id['uid'], $two_critical_sizes[$size_id['uid']]) && is_array($size_id)) {
1419 $critical_sizes[] = $two_critical_sizes[$size_id['uid']];
1420 }
1421 }
1422 }
1423 $data = [
1424 'data' => [
1425 'page_url' => $two_page['url'],
1426 'page_id' => $two_page['id'],
1427 'page_sizes' => $critical_sizes,
1428 'wait_until' => $two_page['wait_until'],
1429 'url_query' => $url_query,
1430 'task' => 'generate',
1431 'newly_connected_website' => $rightAfterConnect,
1432 ],
1433 ];
1434
1435 if (isset($two_page['use_uncritical'])) {
1436 $data['data']['use_uncritical'] = $two_page['use_uncritical'];
1437 }
1438 OptimizerCriticalCss::generateCriticalCSS($data);
1439 $two_critical_pages_count = count($two_critical_pages);
1440 \TenWebWpTransients\OptimizerTransients::set('two_regenerate_critical_data', $two_critical_pages, 60 * $two_critical_pages_count);
1441 }
1442 }
1443 }
1444
1445 public static function get_critical_default_sizes($critical_defaults)
1446 {
1447 global $TwoSettings;
1448 $new_sizes = [];
1449 $two_critical_sizes = $TwoSettings->get_settings('two_critical_sizes');
1450
1451 if (is_array($critical_defaults) && isset($critical_defaults['default_sizes'])) {
1452 foreach ($critical_defaults['default_sizes'] as $key) {
1453 if (isset($two_critical_sizes[$key])) {
1454 $new_sizes[$key] = $two_critical_sizes[$key];
1455 }
1456 }
1457 }
1458
1459 if (!empty($new_sizes)) {
1460 return array_keys($new_sizes);
1461 }
1462
1463 return array_keys($two_critical_sizes);
1464 }
1465
1466 public static function update_critical_statuses($two_critical_pages, $status)
1467 {
1468 global $TwoSettings;
1469
1470 foreach ($two_critical_pages as $key => $two_page) {
1471 $two_critical_pages[$key]['status'] = $status;
1472 }
1473 $TwoSettings->update_setting('two_critical_pages', $two_critical_pages);
1474 }
1475
1476 public static function init_defines()
1477 {
1478 global $TwoSettings;
1479
1480 if (!defined('TWO_LAZYLOAD')) {
1481 define('TWO_LAZYLOAD', true);
1482 }
1483
1484 if (!defined('TWO_WP_SITE_URL')) {
1485 if (function_exists('domain_mapping_siteurl')) {
1486 define('TWO_WP_SITE_URL', domain_mapping_siteurl(get_current_blog_id()));
1487 } else {
1488 define('TWO_WP_SITE_URL', site_url());
1489 }
1490 }
1491
1492 if (!defined('TWO_WP_CONTENT_URL')) {
1493 if (function_exists('get_original_url')) {
1494 define('TWO_WP_CONTENT_URL', str_replace(get_original_url(TWO_WP_SITE_URL), TWO_WP_SITE_URL, content_url()));
1495 } else {
1496 define('TWO_WP_CONTENT_URL', content_url());
1497 }
1498 }
1499
1500 if (!defined('TWO_WP_CONTENT_NAME')) {
1501 define('TWO_WP_CONTENT_NAME', '/' . wp_basename(WP_CONTENT_DIR));
1502 }
1503
1504 if (!defined('TWO_WP_ROOT_URL')) {
1505 define('TWO_WP_ROOT_URL', str_replace(TWO_WP_CONTENT_NAME, '', TWO_WP_CONTENT_URL));
1506 }
1507
1508 if (!defined('TWO_CACHE_URL')) {
1509 if (is_multisite()) {
1510 $blog_id = get_current_blog_id();
1511 define('TWO_CACHE_URL', TWO_WP_CONTENT_URL . TENWEB_SO_CACHE_CHILD_DIR . $blog_id . '/');
1512 } else {
1513 define('TWO_CACHE_URL', TWO_WP_CONTENT_URL . TENWEB_SO_CACHE_CHILD_DIR);
1514 }
1515 }
1516
1517 if (!defined('WP_ROOT_DIR')) {
1518 define('WP_ROOT_DIR', substr(WP_CONTENT_DIR, 0, strlen(WP_CONTENT_DIR) - strlen(TWO_WP_CONTENT_NAME)));
1519 }
1520
1521 if (!defined('TWO_HASH')) {
1522 define('TWO_HASH', wp_hash(TWO_CACHE_URL));
1523 }
1524
1525 if (!defined('TWO_CACHE_NOGZIP')) {
1526 $two_gzip = $TwoSettings->get_settings('two_gzip');
1527
1528 if (!TENWEB_SO_HOSTED_ON_10WEB && $two_gzip === 'on') {
1529 define('TWO_CACHE_NOGZIP', false);
1530 } else {
1531 define('TWO_CACHE_NOGZIP', true);
1532 }
1533 }
1534
1535 if (!defined('TWO_CACHEFILE_PREFIX')) {
1536 define('TWO_CACHEFILE_PREFIX', 'two_');
1537 }
1538 }
1539
1540 public static function flushCloudflareCache($postId = null)
1541 {
1542 if (class_exists('\CF\WordPress\Hooks')) {
1543 $cloudflareHooks = new \CF\WordPress\Hooks();
1544
1545 if (is_int($postId)) {
1546 $cloudflareHooks->purgeCacheByRelevantURLs($postId);
1547 } else {
1548 $cloudflareHooks->purgeCacheEverything();
1549 }
1550 }
1551 }
1552
1553 public static function get_tenweb_connection_link($endpoint = 'sign-up', $args = [])
1554 {
1555 // copied from manager.py
1556 $return_url = get_admin_url() . 'admin.php';
1557
1558 if (is_multisite()) {
1559 $return_url = network_admin_url() . 'admin.php';
1560 }
1561
1562 $token = wp_create_nonce('two_10web_connection');
1563 update_site_option(TW_OPTIMIZE_PREFIX . '_saved_nonce', $token);
1564 $return_url_args = ['page' => 'two_settings_page'];
1565 $register_url_args = [
1566 'site_url' => urlencode(get_site_url()),
1567 'utm_source' => '10webspeedoptimizer',
1568 'from_plugin' => self::FROM_PLUGIN,
1569 'utm_medium' => 'freeplugin',
1570 'nonce' => $token,
1571 'subscr_id' => TENWEB_SO_FREE_SUBSCRIPTION_ID,
1572 'version' => TENWEB_SO_VERSION,
1573 'new_connection_flow' => 1,
1574 ];
1575
1576 if (!empty($args['old_connection_flow'])) {
1577 unset($register_url_args['new_connection_flow']);
1578 }
1579
1580 if (!empty($args)) {
1581 $register_url_args = $register_url_args + $args;
1582 $return_url_args = $return_url_args + $args;
1583 }
1584
1585 $register_url_args['return_url'] = urlencode(add_query_arg($return_url_args, $return_url));
1586
1587 $plugin_from = get_site_option('tenweb_manager_installed');
1588
1589 if ($plugin_from !== false) {
1590 $plugin_from = json_decode($plugin_from, true);
1591
1592 if (is_array($plugin_from) && reset($plugin_from) !== false) {
1593 $register_url_args['plugin_id'] = reset($plugin_from);
1594
1595 if (isset($plugin_from['type'])) {
1596 $register_url_args['utm_source'] = $plugin_from['type'];
1597 }
1598 }
1599 }
1600
1601 $url = add_query_arg($register_url_args, TENWEB_DASHBOARD . '/' . $endpoint . '/');
1602
1603 return $url;
1604 }
1605
1606 public static function getCriticalPages()
1607 {
1608 global $TwoSettings;
1609
1610 if (empty($TwoSettings)) {
1611 return [];
1612 }
1613 $two_critical_pages_from_options = $TwoSettings->get_settings('two_critical_pages');
1614 $two_critical_pages = self::get_meta_values('two_critical_pages');
1615
1616 if ($two_critical_pages_from_options) {
1617 $two_critical_pages = array_replace($two_critical_pages_from_options, $two_critical_pages);
1618 }
1619
1620 return $two_critical_pages;
1621 }
1622
1623 public static function stripslashes_deep($value)
1624 {
1625 // copied from wp-includes/formatting.php
1626 return self::map_deep($value, function ($value) {
1627 return is_string($value) ? stripslashes($value) : $value;
1628 });
1629 }
1630
1631 public static function map_deep($value, $callback)
1632 {
1633 // copied from wp-includes/formatting.php
1634
1635 if (is_array($value)) {
1636 foreach ($value as $index => $item) {
1637 $value[$index] = self::map_deep($item, $callback);
1638 }
1639 } elseif (is_object($value)) {
1640 $object_vars = get_object_vars($value);
1641
1642 foreach ($object_vars as $property_name => $property_value) {
1643 $value->$property_name = self::map_deep($property_value, $callback);
1644 }
1645 } else {
1646 $value = call_user_func($callback, $value);
1647 }
1648
1649 return $value;
1650 }
1651
1652 public static function get_meta_values($key = '')
1653 {
1654 if (empty($key)) {
1655 return null;
1656 }
1657 global $wpdb;
1658
1659 $query = $wpdb->get_results($wpdb->prepare("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = %s", $key)); // phpcs:ignore
1660 $result = [];
1661
1662 foreach ($query as $row) {
1663 $result[ $row->post_id ] = unserialize($row->meta_value); // phpcs:ignore
1664 }
1665
1666 return $result;
1667 }
1668
1669 public static function check_if_hosted_website()
1670 {
1671 if (is_file(WPMU_PLUGIN_DIR . '/10web-manager/10web-manager.php')) {
1672 return true;
1673 }
1674
1675 return false;
1676 }
1677
1678 /* WPML functions*/
1679 public static function get_wpml_home_urls()
1680 {
1681 if (!OptimizerUtils::is_wpml_active()) {
1682 return [];
1683 }
1684
1685 $front_page_id = get_option('page_on_front');
1686
1687 // if $front_page_id is empty or 0, it means home page is archive page and there is no translation for that page
1688 if (!$front_page_id) {
1689 return [];
1690 }
1691
1692 $element_id = $front_page_id;
1693 $element_type = get_post_type($front_page_id);
1694
1695 $home_pages = [];
1696 $languages = apply_filters('wpml_active_languages', '');
1697
1698 foreach ($languages as $lang_code => $language_data) {
1699 $post_id = apply_filters('wpml_object_id', $element_id, $element_type, false, $lang_code);
1700
1701 if (!$post_id) {
1702 continue;
1703 }
1704 $home_pages[$lang_code] = [
1705 'post_id' => $post_id,
1706 'permalink' => get_permalink($post_id),
1707 'title' => get_the_title($post_id)
1708 ];
1709 }
1710
1711 return $home_pages;
1712 }
1713
1714 public static function add_wpml_home_pages_into_critical_pages($critical_pages = null, $home_url = null)
1715 {
1716 /*
1717 * The function added home pages generated by WPML into critical_pages list, if home page is in that list. The
1718 * function doesn't generate critical css.
1719 * */
1720
1721 global $TwoSettings;
1722
1723 if ($critical_pages === null) {
1724 $critical_pages = OptimizerUtils::getCriticalPages();
1725 }
1726
1727 if ($home_url === null && $critical_pages['front_page']) {
1728 $home_url = $critical_pages['front_page']['url'];
1729 }
1730
1731 if (!$home_url) {
1732 return $critical_pages;
1733 }
1734
1735 foreach (OptimizerUtils::get_wpml_home_urls() as $lang_code => $post_data) {
1736 if (rtrim($post_data['permalink'], '/') === rtrim($home_url, '/')) {
1737 continue;
1738 }
1739
1740 if (isset($critical_pages[$post_data['id']])) {
1741 continue;
1742 }
1743
1744 $page_data = $critical_pages['front_page'];
1745 $page_data['title'] = $post_data['title'];
1746 $page_data['url'] = $post_data['permalink'];
1747 $page_data['id'] = $post_data['post_id'];
1748 $critical_pages[$post_data['post_id']] = $page_data;
1749 }
1750
1751 $TwoSettings->update_setting('two_critical_pages', $critical_pages);
1752
1753 return $critical_pages;
1754 }
1755
1756 public static function generate_wpml_home_pages_critical_css($data)
1757 {
1758 self::add_wpml_home_pages_into_critical_pages();
1759
1760 $wpml_data = ['data' => $data['data']];
1761 $home_page_url = $data['data']['page_url'];
1762
1763 foreach (self::getCriticalPages() as $page) {
1764 if ($page['url'] == $home_page_url) {
1765 continue;
1766 }
1767
1768 $is_wpml_page = false;
1769
1770 foreach (self::get_wpml_home_urls() as $wpml_page) {
1771 if ($wpml_page['post_id'] == $page['id']) {
1772 $is_wpml_page = true;
1773 break;
1774 }
1775 }
1776
1777 if ($is_wpml_page === false) {
1778 continue;
1779 }
1780
1781 $wpml_data['data']['page_url'] = $page['url'];
1782 $wpml_data['data']['page_id'] = $page['id'];
1783 OptimizerCriticalCss::generateCriticalCSS($wpml_data);
1784 }
1785 }
1786
1787 public static function get_wpml_post_flag_url($post_id)
1788 {
1789 if ($post_id == 'front_page') {
1790 $post_id = get_option('page_on_front');
1791 }
1792
1793 if (!$post_id) {
1794 $lang_code = apply_filters('wpml_default_language', null);
1795 } else {
1796 $post_language_details = apply_filters('wpml_post_language_details', null, $post_id);
1797 $lang_code = $post_language_details['language_code'];
1798 }
1799
1800 return plugins_url('sitepress-multilingual-cms/res/flags/' . $lang_code . '.png');
1801 }
1802
1803 public static function is_wpml_active()
1804 {
1805 return defined('ICL_SITEPRESS_VERSION');
1806 }
1807
1808 public static function get_modes($name = null, $preview = false, $level = null)
1809 {
1810 $modes = self::MODES;
1811
1812 if ($preview) {
1813 foreach ($modes as $key => $mode) {
1814 $modes[$key]['preview_url'] = add_query_arg(['two_preview' => '1', 'two_level' => $mode['level']], get_home_url() . '/');
1815 }
1816
1817 return $modes;
1818 }
1819
1820 if (isset($name)) {
1821 return self::MODES[$name];
1822 } elseif (isset($level)) {
1823 foreach ($modes as $key => $mode) {
1824 if ($mode['level'] === $level) {
1825 return $mode;
1826 }
1827 }
1828 }
1829 $global_mode = get_option('two_default_mode', OptimizerUtils::MODES['extreme']);
1830
1831 if (is_array($global_mode)) {
1832 $modes['global'] = $global_mode;
1833 }
1834
1835 return $modes;
1836 }
1837
1838 public static function testWebPDelivery()
1839 {
1840 $requestUrl = TENWEB_SO_URL . '/test/webp_test.jpg';
1841 $requestArgs = [
1842 'headers' => [
1843 'ACCEPT' => 'image/webp'
1844 ]
1845 ];
1846 global $TwoSettings;
1847 $wpResult = wp_remote_get($requestUrl, $requestArgs); // phpcs:ignore
1848
1849 if (!is_wp_error($wpResult)) {
1850 if (isset($wpResult['headers']['content-type']) && 'image/webp' === $wpResult['headers']['content-type']) {
1851 $TwoSettings->update_setting('two_webp_delivery_working', '1');
1852
1853 return true;
1854 }
1855 }
1856 $TwoSettings->update_setting('two_webp_delivery_working', '0');
1857
1858 return false;
1859 }
1860
1861 public static function clear_third_party_cache()
1862 {
1863 global $wp_fastest_cache, $kinsta_cache, $nginx_purger;
1864
1865 // if W3 Total Cache is being used, clear the cache
1866 if (function_exists('w3tc_pgcache_flush')) {
1867 w3tc_pgcache_flush();
1868 }
1869
1870 // if WP Super Cache is being used, clear the cache
1871 if (function_exists('wp_cache_clean_cache')) {
1872 global $file_prefix, $supercachedir;
1873
1874 if (empty($supercachedir) && function_exists('get_supercache_dir')) {
1875 $supercachedir = get_supercache_dir();
1876 }
1877 wp_cache_clean_cache($file_prefix);
1878 }
1879
1880 if (class_exists('WpeCommon')) {
1881 //be extra careful, just in case 3rd party changes things on us
1882 if (method_exists('WpeCommon', 'purge_memcached')) {
1883 WpeCommon::purge_memcached();
1884 }
1885
1886 if (method_exists('WpeCommon', 'clear_maxcdn_cache')) {
1887 WpeCommon::clear_maxcdn_cache();
1888 }
1889
1890 if (method_exists('WpeCommon', 'purge_varnish_cache')) {
1891 WpeCommon::purge_varnish_cache();
1892 }
1893 }
1894
1895 if (method_exists('WpFastestCache', 'deleteCache') && !empty($wp_fastest_cache)) {
1896 $wp_fastest_cache->deleteCache(true);
1897 }
1898
1899 if (class_exists('\Kinsta\Cache') && !empty($kinsta_cache) && !empty($kinsta_cache->kinsta_cache_purge)) {
1900 if (method_exists($kinsta_cache->kinsta_cache_purge, 'purge_complete_caches')) {
1901 $kinsta_cache->kinsta_cache_purge->purge_complete_caches();
1902 }
1903 }
1904
1905 if (class_exists('\WPaaS\Cache')) {
1906 if (!\WPaaS\Cache::has_ban()) {
1907 remove_action('shutdown', [ '\WPaaS\Cache', 'purge' ], PHP_INT_MAX);
1908 add_action('shutdown', [ '\WPaaS\Cache', 'ban' ], PHP_INT_MAX);
1909 }
1910 }
1911
1912 if (class_exists('WP_Optimize') && defined('WPO_PLUGIN_MAIN_PATH')) {
1913 if (!class_exists('WP_Optimize_Cache_Commands')) {
1914 include_once WPO_PLUGIN_MAIN_PATH . 'cache/class-cache-commands.php';
1915 }
1916
1917 if (class_exists('WP_Optimize_Cache_Commands')) {
1918 $wpoptimize_cache_commands = new WP_Optimize_Cache_Commands();
1919 $wpoptimize_cache_commands->purge_page_cache();
1920 }
1921 }
1922
1923 if (class_exists('Breeze_Admin')) {
1924 do_action('breeze_clear_all_cache');
1925 }
1926
1927 if (defined('LSCWP_V')) {
1928 do_action('litespeed_purge_all');
1929 }
1930
1931 // This function clears the Site Ground cache only if it is on in plugin.
1932 // As their hosting does not respect plugin's settings sometimes, maybe we'll need to call them directly.
1933 if (function_exists('sg_cachepress_purge_everything')) {
1934 sg_cachepress_purge_everything();
1935 }
1936
1937 if (class_exists('autoptimizeCache')) {
1938 autoptimizeCache::clearall();
1939 }
1940
1941 if (class_exists('Cache_Enabler')) {
1942 Cache_Enabler::clear_total_cache();
1943 }
1944
1945 if (defined('NGINX_HELPER_BASEPATH') && !empty($nginx_purger)) {
1946 $nginx_purger->purge_all();
1947 }
1948
1949 if (function_exists('rocket_clean_domain')) {
1950 rocket_clean_domain();
1951 }
1952
1953 if (defined('EZOIC_CACHE') && EZOIC_CACHE) {
1954 $cache = new \Ezoic_Namespace\Ezoic_Integration_Cache();
1955 $cache->Clear();
1956 }
1957
1958 if (class_exists('Endurance_Page_Cache')) {
1959 $epc = new Endurance_Page_Cache();
1960 $epc->force_purge = true;
1961 $epc->purge_all();
1962 }
1963 }
1964
1965 public static function two_flatten(array $array)
1966 {
1967 $return = [];
1968 array_walk_recursive($array, function ($a) use (&$return) {
1969 $return[] = $a;
1970 });
1971
1972 return $return;
1973 }
1974
1975 public static function triggerPostOptimizationTasks($disabled_incompatible_plugins = [])
1976 {
1977 //todo new_flow_process this should be triggered manually when user clicks the apply mode button
1978 //todo new_flow_process disable test mode, apply mode, call post-optimization
1979 global $TwoSettings;
1980 $flow_id = get_site_option(TENWEB_PREFIX . '_flow_id');
1981 $domain_id = get_site_option('tenweb_domain_id');
1982 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
1983 $two_conflicting_plugins = get_site_option('two_conflicting_plugins');
1984
1985 if (!is_array($two_conflicting_plugins)) {
1986 $two_conflicting_plugins = [];
1987 }
1988
1989 if ($access_token && $domain_id) {
1990 $response = wp_remote_post(TENWEB_SO_CRITICAL_URL . '/v1/workspaces/performance/' . $domain_id . '/post-optimization-tasks', [
1991 'redirection' => 15,
1992 'blocking' => false,
1993 'headers' => [
1994 'accept' => 'application/x.10webperformance.v1+json',
1995 'authorization' => 'Bearer ' . $access_token,
1996 ],
1997 'body' => [
1998 'notification_id' => sanitize_text_field($_POST['notification_id']), // phpcs:ignore
1999 'flow_id' => $flow_id,
2000 'disabled_incompatible_plugins' => $disabled_incompatible_plugins,
2001 'incompatible_plugins' => $two_conflicting_plugins,
2002 'has_excluded_slider' => false
2003 ],
2004 'cookies' => []
2005 ]);
2006
2007 update_option('two_critical_data_import_response_' . time(), [
2008 !is_wp_error($response) ? $response['body'] : $response->get_error_message(),
2009 wp_remote_retrieve_response_code($response)
2010 ], false);
2011 }
2012 }
2013
2014 public static function check_score()
2015 {
2016 $domain_id = get_site_option('tenweb_domain_id');
2017 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
2018 $workspace_id = get_site_option(TENWEB_PREFIX . '_workspace_id');
2019
2020 if ($access_token && $domain_id && $workspace_id) {
2021 wp_remote_post(TENWEB_SO_CRITICAL_URL . '/v1/workspaces/' . $workspace_id . '/performance/' . $domain_id . '/googlepagespeed', [
2022 'redirection' => 15,
2023 'blocking' => false,
2024 'headers' => [
2025 'accept' => 'application/x.10webperformance.v1+json',
2026 'authorization' => 'Bearer ' . $access_token,
2027 ],
2028 'body' => [
2029 ],
2030 'cookies' => []
2031 ]);
2032 }
2033 }
2034
2035 public static function set_global_mode()
2036 {
2037 global $TwoSettings;
2038 $modes = self::get_modes();
2039 $two_critical_status = $TwoSettings->get_settings('two_critical_status');
2040 $two_delay_all_js_execution = $TwoSettings->get_settings('two_delay_all_js_execution');
2041
2042 if ($two_critical_status == 'true' && $two_delay_all_js_execution == 'on') {
2043 $mode = 'extreme';
2044 } elseif ($two_critical_status == 'true' && $two_delay_all_js_execution != 'on') {
2045 $mode = 'balanced';
2046 } elseif ($two_critical_status != 'true' && $two_delay_all_js_execution === 'on') {
2047 $mode = 'strong';
2048 } else {
2049 $mode = 'standard';
2050 }
2051
2052 if (isset($modes[$mode])) {
2053 foreach ($modes[$mode] as $key => $val) {
2054 if (isset($settings_names[$key])) {
2055 $TwoSettings->update_setting($key, $val);
2056 } elseif ($key === 'critical_enabled') {
2057 if ($val) {
2058 $TwoSettings->update_setting('two_critical_status', 'true');
2059 } else {
2060 $TwoSettings->update_setting('two_critical_status', '');
2061 }
2062 }
2063 }
2064 update_option('two_default_mode', $modes[$mode]);
2065 }
2066 }
2067
2068 /**
2069 * @return int|string page_id|front_page|term_$term_id
2070 */
2071 public static function get_current_post_info()
2072 {
2073 $queried_object = get_queried_object();
2074 $id = 0;
2075
2076 if (is_front_page()) {
2077 $id = 'front_page';
2078 } elseif (class_exists('WooCommerce') && is_shop()) {
2079 $id = wc_get_page_id('shop');
2080 } elseif ($queried_object instanceof WP_Term) {
2081 $id = 'term_' . $queried_object->term_id;
2082 } elseif ($queried_object instanceof WP_User) {
2083 $id = 'user_' . $queried_object->ID;
2084 } elseif ($queried_object instanceof \WP_Post) {
2085 $id = $queried_object->ID;
2086 }
2087
2088 if ($id === 0) {
2089 global $wp;
2090 $home_url = trailingslashit(home_url($wp->request));
2091 $db_home_url = trailingslashit(get_home_url());
2092
2093 if ($home_url === $db_home_url) {
2094 $id = 'front_page';
2095 }
2096 }
2097
2098 if ($id === 0 && isset($queried_object->has_archive)) {
2099 global $TwoSettings;
2100
2101 if ($TwoSettings->get_settings('two_optimize_archive_pages') == 'on') {
2102 $id = 'no_critical_' . md5($home_url);
2103 }
2104 }
2105
2106 return $id;
2107 }
2108
2109 public static function get_permalink_name_by_id($id = 'front_page')
2110 {
2111 if ('front_page' === $id) {
2112 return [ 'url' => home_url(), 'title' => 'Home' ];
2113 } else {
2114 if (0 === strpos($id, 'term_')) {
2115 $term_id = (int) ltrim ($id, 'term_');
2116 $term = get_term($term_id);
2117
2118 return [ 'url' => get_term_link($term_id), 'title' => $term->name ];
2119 } elseif (0 === strpos($id, 'user_')) {
2120 $user_id = (int) ltrim ($id, 'user_');
2121
2122 return [ 'url' => get_author_posts_url($user_id), 'title' => get_the_author_meta('nicename', $user_id) ];
2123 } else {
2124 return [ 'url' => get_permalink($id), 'title' => get_the_title($id) ];
2125 }
2126 }
2127 }
2128
2129 public static function remove_url_protocol($url)
2130 {
2131 $url = rtrim($url, '/');
2132 $disallowed = ['http://www.', 'https://www.', 'http://', 'https://'];
2133
2134 foreach ($disallowed as $d) {
2135 if (strpos($url, $d) === 0) {
2136 return str_replace($d, '', $url);
2137 }
2138 }
2139
2140 return $url;
2141 }
2142
2143 public static function get_post_id($page_url = null)
2144 {
2145 $pageUrlHash = md5((string) $page_url); // this function is called in a loop to replace backgrounds, in order not to make a thousand requests to website to find page_id we will cache it in globals
2146 //phpcs:ignore PHPCompatibility.Variables.ForbiddenGlobalVariableVariable.NonBareVariableFound
2147 global ${'two_current_page_id' . $pageUrlHash};
2148
2149 if (!empty(${'two_current_page_id' . $pageUrlHash})) {
2150 return ${'two_current_page_id' . $pageUrlHash};
2151 }
2152 $home_url = rtrim(get_home_url(), '/');
2153 $page_url = rtrim((string) $page_url, '/');
2154 $id = 0;
2155
2156 if (!empty($page_url)) {
2157 if (self::remove_url_protocol($home_url) === self::remove_url_protocol($page_url)) {
2158 return 'front_page';
2159 }
2160 $id = url_to_postid($page_url); // phpcs:ignore
2161
2162 if ($id === 0 && class_exists('WooCommerce')) {
2163 $shop_page_id = wc_get_page_id('shop');
2164 $shop_page_url = rtrim(get_permalink(wc_get_page_id('shop')), '/');
2165
2166 if ($shop_page_url === $page_url) {
2167 $id = $shop_page_id;
2168 }
2169 }
2170
2171 if ($id === 0) {
2172 $page_for_posts = get_option('page_for_posts');
2173 $post_page_id = (int) $page_for_posts;
2174 $post_page_url = rtrim(get_permalink($post_page_id), '/');
2175
2176 if ($post_page_url === $page_url) {
2177 $id = $post_page_id;
2178 }
2179 }
2180
2181 if (0 === $id) {
2182 $page_url_host = wp_parse_url($page_url, PHP_URL_HOST);
2183 $site_url_host = wp_parse_url(get_site_url(), PHP_URL_HOST);
2184
2185 if ($page_url_host === $site_url_host) {
2186 $page_headers = wp_get_http_headers(add_query_arg([ 'two_detect_post_id' => '1' ], $page_url));
2187 $id = isset($page_headers[ 'x-two-post-id' ]) ? $page_headers[ 'x-two-post-id' ] : 0;
2188 }
2189 }
2190 ${'two_current_page_id' . $pageUrlHash} = $id;
2191 } else {
2192 ${'two_current_page_id' . $pageUrlHash} = self::get_current_post_info();
2193 }
2194
2195 return ${'two_current_page_id' . $pageUrlHash};
2196 }
2197
2198 public static function two_update_subscription()
2199 {
2200 $tenweb_subscription_id = false;
2201 $tenweb_plan_title = false;
2202 $domain_id = get_site_option('tenweb_domain_id');
2203 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
2204 $workspace_id = get_site_option(TENWEB_PREFIX . '_workspace_id');
2205
2206 if (!empty($access_token) && !empty($domain_id) && !empty($workspace_id)) {
2207 $response = wp_remote_post(TENWEB_SO_CRITICAL_URL . '/v1/workspaces/' . $workspace_id . '/domains/' . $domain_id . '/get_subscription', [
2208 'timeout' => 5, // phpcs:ignore
2209 'redirection' => 5,
2210 'httpversion' => '1.0',
2211 'blocking' => true,
2212 'headers' => [
2213 'accept' => 'application/x.10webperformance.v1+json',
2214 'authorization' => 'Bearer ' . $access_token,
2215 ],
2216 'body' => [],
2217 'cookies' => []
2218 ]);
2219
2220 if (!is_wp_error($response) && isset($response['body'], $response['response']['code']) && $response['response']['code'] === 200) {
2221 $response_body = json_decode($response['body'], true);
2222
2223 if ($response_body['data']['success']) {
2224 if (isset($response_body['data']['subscription_id'])) {
2225 $tenweb_subscription_id = $response_body['data']['subscription_id'];
2226 \TenWebWpTransients\OptimizerTransients::set(TENWEB_PREFIX . '_subscription_id', $tenweb_subscription_id, 12 * HOUR_IN_SECONDS);
2227 }
2228
2229 if (isset($response_body['data']['plan_title'])) {
2230 $tenweb_plan_title = $response_body['data']['plan_title'];
2231 $tenweb_plan_title = strtolower($tenweb_plan_title) == 'speed' ? 'Free' : $tenweb_plan_title; //sometimes we get 'speed' from service, it means free
2232
2233 if (in_array((int) $tenweb_subscription_id, TENWEB_SO_FREE_SUBSCRIPTION_IDS)) {
2234 $tenweb_plan_title = 'Free';
2235 }
2236 \TenWebWpTransients\OptimizerTransients::set(TENWEB_PREFIX . '_plan_title', $tenweb_plan_title, 12 * HOUR_IN_SECONDS);
2237 }
2238
2239 if (isset($response_body['data']['referral_hash'])) {
2240 $referral_hash = $response_body['data']['referral_hash'];
2241
2242 if (!empty($referral_hash)) {
2243 update_site_option(TENWEB_PREFIX . '_client_referral_hash', $referral_hash);
2244 }
2245 }
2246 }
2247 } elseif (is_wp_error($response) || !isset($response['body'], $response['response']['code']) || (string) $response['response']['code'] !== '429') {
2248 \TenWebWpTransients\OptimizerTransients::set(TENWEB_PREFIX . '_subscription_id', '0', HOUR_IN_SECONDS);
2249 \TenWebWpTransients\OptimizerTransients::set(TENWEB_PREFIX . '_plan_title', '', HOUR_IN_SECONDS);
2250 }
2251
2252 if (!is_wp_error($response) && isset($response['response']['code'])) {
2253 \TenWebWpTransients\OptimizerTransients::set(TENWEB_PREFIX . '_subscription_response_code', (string) $response['response']['code'], HOUR_IN_SECONDS);
2254 }
2255 }
2256
2257 return compact('tenweb_subscription_id', 'tenweb_plan_title');
2258 }
2259
2260 public static function two_critical_status($page_id = false)
2261 {
2262 global $TwoSettings;
2263 $two_critical_pages = self::getCriticalPages();
2264
2265 if (is_array($two_critical_pages)) {
2266 if ($page_id === false) {
2267 foreach ($two_critical_pages as $critical_page) {
2268 self::two_critical_status($critical_page['id']);
2269 }
2270 } elseif (isset($two_critical_pages[$page_id])) {
2271 $critical_in_progress_key = 'two_critical_in_progress_' . $page_id;
2272 $critical_in_progress = \TenWebWpTransients\OptimizerTransients::get($critical_in_progress_key);
2273
2274 if ($critical_in_progress !== '1') {
2275 if (isset($two_critical_pages[$page_id]['status']) && $two_critical_pages[$page_id]['status'] == 'in_progress') {
2276 $two_critical_pages[$page_id]['status'] = 'not_started';
2277 $TwoSettings->update_setting('two_critical_pages', $two_critical_pages);
2278 }
2279 }
2280 }
2281 }
2282 }
2283
2284 public static function two_redirect($url, $exit = true)
2285 {
2286 while (ob_get_level() !== 0) {
2287 ob_end_clean();
2288 }
2289 wp_redirect($url); //phpcs:ignore WordPressVIPMinimum.Security.ExitAfterRedirect.NoExit
2290
2291 if ($exit) {
2292 exit();
2293 }
2294 }
2295
2296 public static function check_page_has_no_redirects($url, $arg = true)
2297 {
2298 if ($arg) {
2299 $url = add_query_arg([
2300 'two_check_redirect' => '1',
2301 ], $url);
2302 }
2303 $response = wp_remote_head($url, [
2304 'timeout' => 20, // phpcs:ignore
2305 'redirection' => 0,
2306 'httpversion' => '1.0',
2307 'blocking' => true,
2308 'sslverify' => false
2309 ]);
2310
2311 if (!is_wp_error($response)) {
2312 $headers = $response[ 'headers' ];
2313
2314 if (isset($headers[ 'Location' ])) {
2315 $location = $headers[ 'Location' ];
2316
2317 if (!empty($location)) {
2318 if (is_array($location)) {
2319 $location = end($location);
2320 }
2321 $redirect_url_parse = wp_parse_url($location);
2322 $main_url_parse = wp_parse_url($url);
2323
2324 if (isset($redirect_url_parse[ 'host' ]) && isset($main_url_parse[ 'host' ])) {
2325 if ($redirect_url_parse[ 'host' ] === $main_url_parse[ 'host' ]) {
2326 return true;
2327 }
2328 }
2329
2330 return false;
2331 }
2332
2333 return false;
2334 }
2335 }
2336
2337 return true;
2338 }
2339
2340 public static function is_paid_user()
2341 {
2342 global $tenweb_subscription_id;
2343
2344 return (defined('TENWEB_SO_HOSTED_ON_10WEB') && TENWEB_SO_HOSTED_ON_10WEB) || (defined('TENWEB_SO_FREE_SUBSCRIPTION_IDS') && (!in_array((int) $tenweb_subscription_id, TENWEB_SO_FREE_SUBSCRIPTION_IDS)));
2345 }
2346
2347 public static function is_tenweb_booster_connected()
2348 {
2349 return (defined('TENWEB_SO_HOSTED_ON_10WEB') && TENWEB_SO_HOSTED_ON_10WEB) ||
2350 (defined('TENWEB_CONNECTED_SPEED')
2351 && \Tenweb_Authorization\Login::get_instance()->check_logged_in()
2352 && \Tenweb_Authorization\Login::get_instance()->get_connection_type() == TENWEB_CONNECTED_SPEED
2353 && !empty(get_option('two_first_connect')));
2354 }
2355
2356 /**
2357 * For hosting cache , run only connected sites
2358 **/
2359 public static function update_post($id = 0)
2360 {
2361 if (TENWEB_SO_HOSTED_ON_10WEB) {
2362 return;
2363 }
2364
2365 if ($id === 0) {
2366 $id = get_option('page_on_front');
2367
2368 if ($id === '0' || $id === 0) {
2369 $recent_post = wp_get_recent_posts([
2370 'numberposts' => 1,
2371 'post_type' => 'post',
2372 'post_status' => 'publish',
2373 ], OBJECT);
2374
2375 if (is_array($recent_post) && isset($recent_post[0]->ID)) {
2376 $id = $recent_post[0]->ID;
2377 }
2378 }
2379 }
2380
2381 if ($id !== 0) {
2382 $post_data = ['ID' => $id];
2383 // removing kses_filter to avoid striping script or style tags from gutenberg editor
2384 remove_filter('content_save_pre', 'wp_filter_post_kses');
2385 wp_update_post(wp_slash($post_data));
2386 add_filter('content_save_pre', 'wp_filter_post_kses');
2387 }
2388 }
2389
2390 /**
2391 * For not hosted sites
2392 **/
2393 public static function set_critical()
2394 {
2395 if (isset($_POST['token'], $_POST['page_id']) && get_option('two_critical' . sanitize_text_field($_POST['page_id'])) === $_POST['token']) { // phpcs:ignore
2396 \TenWebWpTransients\OptimizerTransients::delete('two_critical' . sanitize_text_field($_POST['page_id'])); // phpcs:ignore
2397
2398 if (isset($_FILES['covered_css']) && isset($_FILES['covered_css']['tmp_name'])) {
2399 //TODO: maybe sanitization must be implemented
2400 $uploadfile = $_FILES['covered_css']['tmp_name']; // phpcs:ignore
2401 \TenWebWpTransients\OptimizerTransients::delete('two_critical_in_process');
2402 $triggerPostOptimizationTasks = !empty($_POST['newly_connected_website']) && !empty($_POST['notification_id']); // phpcs:ignore
2403 update_option('two_critical_data_import_data_' . time(), $triggerPostOptimizationTasks, false);
2404 \TenWebOptimizer\OptimizerCriticalCss::createCriticalCSS($uploadfile, $triggerPostOptimizationTasks);
2405 echo '{"status":"ok"}';
2406 die(0);
2407 }
2408
2409 die('no covered_css data');
2410 }
2411
2412 die('Invalid token');
2413 }
2414
2415 public static function download_critical()
2416 {
2417 if (isset($_GET['two_update_critical'], $_GET['page_id']) && $_GET['two_update_critical'] === '1') { // phpcs:ignore
2418 $return_data = [
2419 'success' => false,
2420 'message' => 'error'
2421 ];
2422 $page_id = sanitize_text_field($_GET['page_id']); // phpcs:ignore
2423 $triggerPostOptimizationTasks = !empty($_GET['newly_connected_website']) && !empty($_GET['notification_id']); // phpcs:ignore
2424 $domain_id = get_site_option('tenweb_domain_id');
2425 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
2426 $file_content_response = wp_remote_get(TENWEB_SO_CRITICAL_URL . '/v1/critical/' . $domain_id . '/pages/' . $page_id . '/get', [ // phpcs:ignore
2427 'timeout' => 5, // phpcs:ignore
2428 'redirection' => 5,
2429 'httpversion' => '1.0',
2430 'blocking' => true,
2431 'headers' => [
2432 'accept' => 'application/x.10webperformance.v1+json',
2433 'authorization' => 'Bearer ' . $access_token,
2434 ],
2435 'cookies' => []
2436 ]);
2437
2438 if (!is_wp_error($file_content_response) && isset($file_content_response['body'])) {
2439 $file_content = $file_content_response['body'];
2440 $file_content_arr = json_decode($file_content, true);
2441
2442 if (isset($file_content_arr['data']['data']['covered_css']['value'])) {
2443 \TenWebOptimizer\OptimizerCriticalCss::createCriticalCSS(false, $triggerPostOptimizationTasks, $file_content_arr['data']['data']['covered_css']['value'], true);
2444 $return_data['success'] = true;
2445 $return_data['message'] = 'success';
2446 }
2447 }
2448 echo json_encode($return_data); // phpcs:ignore
2449 die;
2450 }
2451 }
2452
2453 public static function update_connection_flow_progress($status, $step, $metaData = [])
2454 {
2455 $flow_id = get_site_option(TENWEB_PREFIX . '_flow_id');
2456 $notification_id = get_site_option(TENWEB_PREFIX . '_notification_id');
2457 $domain_id = get_site_option('tenweb_domain_id');
2458 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
2459
2460 if ($access_token && $domain_id) {
2461 $response = wp_remote_post(TENWEB_SO_CRITICAL_URL . '/v1/domains/' . $domain_id . '/update_connection_flow_progress', [
2462 'timeout' => 89, // phpcs:ignore
2463 'redirection' => 15,
2464 'blocking' => true,
2465 'headers' => [
2466 'accept' => 'application/x.10webperformance.v1+json',
2467 'authorization' => 'Bearer ' . $access_token,
2468 ],
2469 'body' => [
2470 'flow_id' => $flow_id,
2471 'notification_id' => $notification_id,
2472 'status' => $status,
2473 'step' => $step,
2474 'debug_data' => $metaData
2475 ],
2476 'cookies' => []
2477 ]);
2478 }
2479 }
2480
2481 public static function filter_incompatible_plugins($plugins)
2482 {
2483 $two_incompatible_plugin_list = self::TWO_INCOMPATIBLE_PLUGIN_LIST;
2484 $return_list = [
2485 'incompatible' => [],
2486 'compatible' => [],
2487 ];
2488
2489 foreach ($plugins as $plugin) {
2490 $key = array_search($plugin, $two_incompatible_plugin_list, true);
2491
2492 if ($key) {
2493 $return_list['incompatible'][] = $two_incompatible_plugin_list[$key];
2494 } else {
2495 $return_list['compatible'][] = $plugin;
2496 }
2497 }
2498
2499 return $return_list;
2500 }
2501
2502 public static function get_conflicting_plugins()
2503 {
2504 $two_incompatible_plugin_list = self::TWO_INCOMPATIBLE_PLUGIN_LIST;
2505 $active_plugins = get_option('active_plugins');
2506 $all_plugins = get_plugins();
2507
2508 $incompatible_active_plugin_slugs = array_intersect($two_incompatible_plugin_list, $active_plugins);
2509 $incompatible_active_plugin_list = [];
2510
2511 foreach ($incompatible_active_plugin_slugs as $plugin) {
2512 $incompatible_active_plugin_list[$plugin] = $all_plugins[$plugin]['Name'];
2513 }
2514
2515 return $incompatible_active_plugin_list;
2516 }
2517
2518 public static function injectCriticalBg($content, $critical, $cacheStructure)
2519 {
2520 if (isset($_GET['no_critical_css']) && $_GET['no_critical_css'] == 1) { // phpcs:ignore
2521 return $content;
2522 }
2523 global $TwoSettings;
2524 $two_serve_optimized_bg_image = $TwoSettings->get_settings('two_serve_optimized_bg_image');
2525
2526 if ($two_serve_optimized_bg_image === 'on') {
2527 if (isset($critical) && isset($critical->critical_bg) && !empty($critical->critical_bg)) {
2528 $critical_bg_file_dir = TWO_CACHE_DIR . 'critical/' . $critical->critical_bg;
2529
2530 if (file_exists($critical_bg_file_dir)) {
2531 $critical_bg_data = file_get_contents($critical_bg_file_dir); // phpcs:ignore
2532 $critical_bg_data_arr = json_decode($critical_bg_data, true);
2533 $critical_bg_css = self::serve_different_sizes_for_critical_bg_image($critical_bg_data_arr);
2534
2535 if (!empty($critical_bg_css)) {
2536 $critical_bg_css = "<style id='two_critical_bg' class='two_critical_bg'>" . $critical_bg_css . '</style>';
2537 }
2538 }
2539 }
2540 }
2541
2542 if (isset($critical_bg_css) && !empty($critical_bg_css)) {
2543 $content = OptimizerUtils::inject_in_html($content, $critical_bg_css, ['</head>', 'before']);
2544 $cacheStructure->addToTagsToAdd($critical_bg_css, ['</head>', 'before']);
2545 }
2546
2547 return $content;
2548 }
2549
2550 public static function delete_define($key, $content)
2551 {
2552 $re = '/define\s*\(\s*[\'\"](' . $key . ')[\'\"].?,(.*?)\);/';
2553 preg_match_all($re, $content, $matches, PREG_SET_ORDER, 0);
2554
2555 if (is_array($matches)) {
2556 foreach ($matches as $define_arr) {
2557 if (isset($define_arr[0], $define_arr[1]) && $define_arr[1] == 'WP_CACHE') {
2558 $content = str_replace($define_arr[0], '', $content);
2559 }
2560 }
2561 }
2562
2563 return $content;
2564 }
2565
2566 public static function check_plugin_update()
2567 {
2568 $plugins = get_site_transient('update_plugins');
2569
2570 if (isset($plugins->response) && is_array($plugins->response)) {
2571 foreach ($plugins->response as $plugin_data) {
2572 if (isset($plugin_data->slug) && $plugin_data->slug === 'tenweb-speed-optimizer') {
2573 return true;
2574 }
2575 }
2576 }
2577
2578 return false;
2579 }
2580
2581 public static function clear_cloudflare_cache($prefixes = [], $clear_cache_from = '')
2582 {
2583 global $TwoSettings;
2584 $cloudflare_cache_status = $TwoSettings->get_settings('cloudflare_cache_status');
2585
2586 if (($cloudflare_cache_status !== 'on' || TENWEB_SO_HOSTED_ON_10WEB) && $clear_cache_from != 'settings_page') {
2587 return true;
2588 }
2589 $domain_id = get_site_option('tenweb_domain_id');
2590 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
2591 $workspace_id = get_site_option(TENWEB_PREFIX . '_workspace_id');
2592
2593 $req_body = [];
2594 $req_body['prefixes'] = $prefixes;
2595
2596 if (isset($access_token, $domain_id, $workspace_id) && !empty($access_token) && !empty($domain_id) && !empty($workspace_id)) {
2597 $response = wp_remote_post(TENWEB_SO_CRITICAL_URL . '/v1/workspaces/' . $workspace_id . '/domains/' . $domain_id . '/clear_cloudflare_cache', [
2598 'timeout' => 1,
2599 'redirection' => 5,
2600 'httpversion' => '1.0',
2601 'blocking' => false,
2602 'headers' => [
2603 'accept' => 'application/x.10webperformance.v1+json',
2604 'authorization' => 'Bearer ' . $access_token,
2605 ],
2606 'body' => $req_body,
2607 'cookies' => []
2608 ]);
2609 }
2610
2611 return true;
2612 }
2613
2614 public static function has_changed_bg_image()
2615 {
2616 return self::$has_changed_bg_image;
2617 }
2618
2619 public static function delete_critical_page($page_id)
2620 {
2621 global $TwoSettings;
2622 $critical_key = 'two_critical_' . $page_id;
2623 $critical_in_progress_key = 'two_critical_in_progress_' . $page_id;
2624 \TenWebWpTransients\OptimizerTransients::delete($critical_key);
2625 \TenWebWpTransients\OptimizerTransients::delete($critical_in_progress_key);
2626
2627 $two_critical_all_pages = OptimizerUtils::getCriticalPages();
2628
2629 if (OptimizerUrl::isCriticalSavedInSettings($page_id)) {
2630 $two_critical_pages = $TwoSettings->get_settings('two_critical_pages');
2631 unset($two_critical_pages[$page_id]);
2632 unset($two_critical_pages['']);
2633 $TwoSettings->update_setting('two_critical_pages', $two_critical_pages);
2634 } else {
2635 delete_post_meta($page_id, 'two_critical_pages');
2636 }
2637 $prefix = 'critical/two_' . $page_id . '_*.*';
2638 self::delete_files_by_prefix($prefix);
2639
2640 $tenweb_subscription_id = \TenWebWpTransients\OptimizerTransients::get(TENWEB_PREFIX . '_subscription_id');
2641 $is_free = (in_array((int) $tenweb_subscription_id, TENWEB_SO_FREE_SUBSCRIPTION_IDS) && !TENWEB_SO_HOSTED_ON_10WEB);
2642
2643 if (is_array($two_critical_all_pages) && isset($two_critical_all_pages[$page_id]) && $is_free) {
2644 self::delete_so_page($page_id);
2645 }
2646 }
2647
2648 public static function delete_so_page($page_id)
2649 {
2650 $domain_id = get_site_option('tenweb_domain_id');
2651 $access_token = get_site_option(TENWEB_PREFIX . '_access_token');
2652 $workspace_id = get_site_option(TENWEB_PREFIX . '_workspace_id');
2653
2654 if ($access_token && $domain_id && $workspace_id) {
2655 wp_remote_post(TENWEB_SO_CRITICAL_URL . '/v1/workspaces/' . $workspace_id . '/domains/' . $domain_id . '/delete-so-page', [
2656 'timeout' => 5, // phpcs:ignore
2657 'redirection' => 5,
2658 'httpversion' => '1.0',
2659 'blocking' => true,
2660 'headers' => [
2661 'accept' => 'application/x.10webperformance.v1+json',
2662 'authorization' => 'Bearer ' . $access_token,
2663 ],
2664 'body' => [
2665 'pageId' => $page_id
2666 ],
2667 'cookies' => []
2668 ]);
2669 }
2670 }
2671
2672 public static function update_site_state()
2673 {
2674 $domain_id = get_option('tenweb_domain_id');
2675
2676 if ($domain_id) {
2677 global $wp_version, $wpdb;
2678 $sql_version = $wpdb->get_var('SELECT VERSION() AS version'); // phpcs:ignore
2679 $home_url = get_home_url();
2680 $admin_url = get_admin_url();
2681 $site_title = get_bloginfo('name');
2682 $url = TENWEB_API_URL . '/site-state/' . $domain_id;
2683 $site_info = [
2684 'site_info' => [
2685 'platform' => 'wordpress',
2686 'site_url' => $home_url,
2687 'admin_url' => $admin_url,
2688 'name' => $home_url,
2689 'site_title' => $site_title,
2690 'site_screenshot_url' => $home_url,
2691 'platform_version' => $wp_version,
2692 'php_version' => PHP_VERSION,
2693 'mysql_version' => $sql_version,
2694 'manager_version' => get_site_option(TENWEB_PREFIX . '_from_image_optimizer') ? 'iowd_' . TENWEBIO_PREFIX : TENWEB_VERSION,
2695 'other_data' => [
2696 'manager_version' => TENWEB_VERSION,
2697 ]
2698 ]
2699 ];
2700 $args = [
2701 'method' => 'POST',
2702 'body' => ['data' => $site_info]
2703 ];
2704 $Helper = \Tenweb_Authorization\Helper::get_instance();
2705
2706 return $Helper->request($url, $args, 'send_site_state');
2707 }
2708 }
2709
2710 public static function init_flow_score_check($only_do_request = false)
2711 {
2712 if ($only_do_request) {
2713 $nonce = uniqid('two_', false);
2714 update_option('wp_two_nonce_two_init_flow_score', $nonce);
2715 $res = wp_remote_post(admin_url('admin-ajax.php'), [
2716 'timeout' => 5, // phpcs:ignore
2717 'redirection' => 5,
2718 'httpversion' => '1.0',
2719 'blocking' => false,
2720 'body' => [
2721 'action' => 'two_init_flow_score',
2722 'nonce' => $nonce
2723 ],
2724 'cookies' => []
2725 ]);
2726
2727 return;
2728 }
2729 $flow_score_check_init = get_option('flow_score_check_init', false);
2730 $two_flow_speed = get_option('two_flow_speed', false);
2731
2732 if ($flow_score_check_init !== '1' && !is_array($two_flow_speed) && !\Tenweb_Authorization\Login::get_instance()->check_logged_in()) {
2733 update_option('flow_score_check_init', '1');
2734 self::add_log_for_score_check_flow('init_flow_score_check', 'start two_google_check_score');
2735 \TenWebSC\TWScoreChecker::twsc_check_score('front_page', $old = true, $no_optimized = true);
2736 $score_data = get_option('two-front-page-speed');
2737 $speed_data['nooptimize_score'] = [];
2738
2739 if (isset($score_data['previous_score']['desktop_score'], $score_data['previous_score']['desktop_tti'])) {
2740 $speed_data['nooptimize_score']['desktopScore'] = $score_data['previous_score']['desktop_score'];
2741 $speed_data['nooptimize_score']['desktopTti'] = $score_data['previous_score']['desktop_tti'];
2742 }
2743
2744 if (isset($score_data['previous_score']['mobile_score'], $score_data['previous_score']['mobile_tti'])) {
2745 $speed_data['nooptimize_score']['mobileScore'] = $score_data['previous_score']['mobile_score'];
2746 $speed_data['nooptimize_score']['mobileTti'] = $score_data['previous_score']['mobile_tti'];
2747 }
2748 $speed_data['nooptimize_score']['tool'] = 'pageSpeedInsight';
2749 $speed_data['nooptimize_score']['two_version'] = TENWEB_SO_VERSION;
2750 $speed_data['nooptimize_score']['desktopData'] = [];
2751 $speed_data['nooptimize_score']['mobileData'] = [];
2752
2753 update_site_option('two_flow_speed', $speed_data);
2754 }
2755 global $wpdb;
2756 $row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", TENWEB_PREFIX . '_access_token')); // phpcs:ignore
2757 $access_token = false;
2758
2759 if (is_object($row) && isset($row->option_value)) {
2760 $access_token = $row->option_value;
2761 }
2762 $two_flow_speed = get_option('two_flow_speed', false);
2763 self::add_log_for_score_check_flow('two_flow_speed', $two_flow_speed);
2764
2765 if ($access_token && is_array($two_flow_speed) && isset($two_flow_speed['nooptimize_score']['mobileScore'], $two_flow_speed['nooptimize_score']['desktopScore'])) {
2766 $domain_id = get_site_option('tenweb_domain_id');
2767 $flow_id = get_site_option(TENWEB_PREFIX . '_flow_id', false);
2768
2769 $workspace_id = get_site_option(TENWEB_PREFIX . '_workspace_id');
2770 $route = TENWEB_SO_CRITICAL_URL . '/v1/workspaces/' . $workspace_id . '/domains/' . $domain_id . '/set_nooptimize_score';
2771 $body = $two_flow_speed;
2772 $body['check_with_no_optimization'] = true;
2773 $body['flow_id'] = $flow_id;
2774 $body['is_first_optimization_flow'] = false;
2775 self::add_log_for_score_check_flow('send_data_to_performance', 'start send_data_to_performance');
2776 self::send_data_to_performance($route, $body, $access_token);
2777 }
2778 }
2779
2780 public static function send_data_to_performance($route, $body, $access_token)
2781 {
2782 $res = wp_remote_post($route, [
2783 'timeout' => 10, // phpcs:ignore
2784 'redirection' => 10,
2785 'httpversion' => '1.0',
2786 'blocking' => true,
2787 'headers' => [
2788 'accept' => 'application/x.10webperformance.v1+json',
2789 'authorization' => 'Bearer ' . $access_token,
2790 ],
2791 'body' => $body,
2792 'cookies' => []
2793 ]);
2794 }
2795
2796 public static function add_log_for_score_check_flow($key, $val)
2797 {
2798 $two_flow_score_log = get_option('two_flow_score_log', []);
2799 $flag = 1;
2800
2801 if (isset($two_flow_score_log['flag'])) {
2802 $flag = (int) $two_flow_score_log['flag'];
2803 $flag ++;
2804 }
2805 $arr_key = $flag . '_' . $key;
2806 $two_flow_score_log[$arr_key] = $val;
2807 $two_flow_score_log['flag'] = $flag;
2808 update_option('two_flow_score_log', $two_flow_score_log);
2809 }
2810
2811 public static function warmup_cache()
2812 {
2813 if (!self::check_if_hosted_website()) {
2814 $site_url = site_url() . '?tenweb_warmup_cache=1&tenweb_version=' . TENWEB_SO_VERSION . '&warmup_time=' . time();
2815 wp_remote_get($site_url, ['sslverify' => false, 'blocking' => false, 'timeout' => 0.1]); // phpcs:ignore
2816 }
2817 }
2818
2819 public static function get_absolute_url($url)
2820 {
2821 $parsed = parse_url($url); // phpcs:ignore
2822 $path = $parsed['path'];
2823
2824 if (!str_contains($path, '..')) {
2825 return $url;
2826 }
2827
2828 // fix dots in relative path
2829 $path_parts = explode('/', $path);
2830 $absolutes = [];
2831
2832 foreach ($path_parts as $part) {
2833 if ('..' == $part) {
2834 array_pop($absolutes);
2835 } else {
2836 $absolutes[] = $part;
2837 }
2838 }
2839
2840 return str_replace($parsed['path'], implode('/', $absolutes), $url);
2841 }
2842
2843 public static function IOConnected()
2844 {
2845 return get_site_option(TENWEB_PREFIX . '_from_image_optimizer');
2846 }
2847
2848 public static function TWOConnected()
2849 {
2850 return get_option('two_first_connect');
2851 }
2852
2853 public static function request_webp_action($task, $url_list = '')
2854 {
2855 try {
2856 if ('regenerate' === $task) {
2857 $image_list = [];
2858 $page_list = [];
2859
2860 foreach (explode(' ', $url_list) as $url) {
2861 if (0 === strpos($url, site_url())) {
2862 if (preg_match('/\.(jpg|png|jpeg)$/', $url)) {
2863 $image_list[] = $url;
2864 } else {
2865 $page_list[] = $url . (strpos($url, '?') > -1 ? '&' : '?') . 'two_nooptimize=1';
2866 }
2867 }
2868 }
2869
2870 if (empty($image_list) || TENWEB_SO_HOSTED_ON_10WEB) {
2871 $request_data = [
2872 'force_convert' => 0,
2873 'quality' => 50,
2874 'image_list' => implode(',', $image_list),
2875 'url_list' => implode(',', $page_list),
2876 'site_url' => site_url(),
2877 ];
2878 $method = 'POST';
2879 $endpoint = \TenWebIO\Api::API_WEBP_CONVERT;
2880 $api_instance = new \TenWebIO\Api($endpoint);
2881 $response = $api_instance->apiRequest($method, $request_data);
2882
2883 if (false !== $response) {
2884 $response_data = [
2885 'status' => 'success',
2886 ];
2887 } else {
2888 $response_data = [
2889 'status' => 'error',
2890 'error' => false
2891 ];
2892 }
2893 } else {
2894 //if we have array of urls, and website is not hosted on 10Web call internal IO classes to optimize them
2895 $compressService = new CompressService();
2896 $compressService->compressCustom($image_list, 'front_page', 1);
2897 $response_data = [
2898 'status' => 'success',
2899 ];
2900 }
2901 } elseif ('delete' === $task) {
2902 $count = \TenWebIO\Utils::deleteWebPImages();
2903 $response_data = [
2904 'status' => 'success',
2905 'count' => $count
2906 ];
2907 } else {
2908 $response_data = [
2909 'status' => 'error',
2910 'error' => 'Invalid Task'
2911 ];
2912 }
2913 } catch (Exception $e) {
2914 $response_data = [
2915 'status' => 'error',
2916 'error' => $e->getMessage()
2917 ];
2918 }
2919
2920 return $response_data; // phpcs:ignore
2921 }
2922
2923 public static function check_admin_capabilities()
2924 {
2925 return current_user_can('manage_options');
2926 }
2927
2928 public static function dirsize($dir)
2929 {
2930 @$dh = opendir($dir);
2931 $size = 0;
2932
2933 if ($dh) {
2934 while ($file = @readdir($dh)) { // phpcs:ignore
2935 if ($file != '.' and $file != '..') {
2936 $path = $dir . '/' . $file;
2937
2938 if (is_dir($path)) {
2939 $size += self::dirsize($path); // recursive in sub-folders
2940 } elseif (is_file($path)) {
2941 $size += filesize($path); // add file
2942 }
2943 }
2944 }
2945 @closedir($dh);
2946 }
2947
2948 return $size;
2949 }
2950
2951 public static function get_booster_icon()
2952 {
2953 if (defined('TENWEB_WHITELABEL_DIR') && is_dir(TENWEB_WHITELABEL_DIR . '/images')) {
2954 $icon_dir = TENWEB_WHITELABEL_DIR . '/images';
2955 $icons = scandir($icon_dir);
2956
2957 //return first element
2958 if (isset($icons[2]) && $icons[2] != '.' && $icons != '..') {
2959 return TENWEB_URL_WHITELABEL . '/images/' . $icons[2];
2960 }
2961 }
2962
2963 return strtolower(TWO_SO_ORGANIZATION_NAME) == '10web' ? TENWEB_SO_URL . '/assets/images/logo_green.svg' : '';
2964 }
2965
2966 /**
2967 * Detect builder type based on active theme
2968 *
2969 * @return string|null Returns 'wvc' for wvc-theme, 'section_based' for tenweb-website-builder-theme, or null
2970 */
2971 public static function detect_builder_type()
2972 {
2973 if (!function_exists('wp_get_theme')) {
2974 return;
2975 }
2976 $active_theme = wp_get_theme();
2977 $theme_slug = $active_theme->get_stylesheet(); // Gets theme directory name
2978 $text_domain = $active_theme->get('TextDomain');
2979 $theme_name = $active_theme->get('Name');
2980
2981 // Check by theme directory/slug or text domain
2982 if ($theme_slug === 'wvc-theme' || $text_domain === 'wvc-theme') {
2983 return 'wvc';
2984 }
2985
2986 if ($theme_slug === 'tenweb-website-builder-theme' || $text_domain === 'tenweb-website-builder-theme') {
2987 return 'section_based';
2988 }
2989
2990 // Fallback: check by theme name
2991 if (stripos($theme_name, 'WordPress AI Builder') !== false ||
2992 stripos($theme_name, 'wvc') !== false) {
2993 return 'wvc';
2994 }
2995
2996 if (stripos($theme_name, 'Builder Theme') !== false) {
2997 return 'section_based';
2998 }
2999
3000 return null;
3001 }
3002 }
3003