PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 7.6.1
LiteSpeed Cache v7.6.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / src / utility.cls.php
litespeed-cache / src Last commit date
cdn 7 months ago data_structure 7 months ago activation.cls.php 7 months ago admin-display.cls.php 7 months ago admin-settings.cls.php 7 months ago admin.cls.php 7 months ago api.cls.php 7 months ago avatar.cls.php 7 months ago base.cls.php 7 months ago cdn.cls.php 7 months ago cloud.cls.php 7 months ago conf.cls.php 7 months ago control.cls.php 7 months ago core.cls.php 7 months ago crawler-map.cls.php 7 months ago crawler.cls.php 7 months ago css.cls.php 7 months ago data.cls.php 7 months ago data.upgrade.func.php 7 months ago db-optm.cls.php 7 months ago debug2.cls.php 7 months ago doc.cls.php 7 months ago error.cls.php 7 months ago esi.cls.php 7 months ago file.cls.php 7 months ago gui.cls.php 7 months ago health.cls.php 7 months ago htaccess.cls.php 7 months ago img-optm.cls.php 7 months ago import.cls.php 7 months ago import.preset.cls.php 7 months ago lang.cls.php 7 months ago localization.cls.php 7 months ago media.cls.php 7 months ago metabox.cls.php 7 months ago object-cache-wp.cls.php 7 months ago object-cache.cls.php 7 months ago object.lib.php 7 months ago optimize.cls.php 7 months ago optimizer.cls.php 7 months ago placeholder.cls.php 7 months ago purge.cls.php 7 months ago report.cls.php 7 months ago rest.cls.php 7 months ago root.cls.php 7 months ago router.cls.php 7 months ago str.cls.php 7 months ago tag.cls.php 7 months ago task.cls.php 7 months ago tool.cls.php 7 months ago ucss.cls.php 7 months ago utility.cls.php 7 months ago vary.cls.php 7 months ago vpi.cls.php 7 months ago
utility.cls.php
959 lines
1 <?php
2 // phpcs:ignoreFile
3
4 /**
5 * The utility class.
6 *
7 * @since 1.1.5
8 */
9
10 namespace LiteSpeed;
11
12 defined('WPINC') || exit();
13
14 class Utility extends Root {
15
16 private static $_internal_domains;
17
18 /**
19 * Validate regex
20 *
21 * @since 1.0.9
22 * @since 3.0 Moved here from admin-settings.cls
23 * @access public
24 * @return bool True for valid rules, false otherwise.
25 */
26 public static function syntax_checker( $rules ) {
27 return preg_match(self::arr2regex($rules), '') !== false;
28 }
29
30 /**
31 * Combine regex array to regex rule
32 *
33 * @since 3.0
34 */
35 public static function arr2regex( $arr, $drop_delimiter = false ) {
36 $arr = self::sanitize_lines($arr);
37
38 $new_arr = array();
39 foreach ($arr as $v) {
40 $new_arr[] = preg_quote($v, '#');
41 }
42
43 $regex = implode('|', $new_arr);
44 $regex = str_replace(' ', '\\ ', $regex);
45
46 if ($drop_delimiter) {
47 return $regex;
48 }
49
50 return '#' . $regex . '#';
51 }
52
53 /**
54 * Replace wildcard to regex
55 *
56 * @since 3.2.2
57 */
58 public static function wildcard2regex( $string ) {
59 if (is_array($string)) {
60 return array_map(__CLASS__ . '::wildcard2regex', $string);
61 }
62
63 if (strpos($string, '*') !== false) {
64 $string = preg_quote($string, '#');
65 $string = str_replace('\*', '.*', $string);
66 }
67
68 return $string;
69 }
70
71 /**
72 * Check if an URL or current page is REST req or not
73 *
74 * @since 2.9.3
75 * @deprecated 2.9.4 Moved to REST class
76 * @access public
77 */
78 public static function is_rest( $url = false ) {
79 return false;
80 }
81
82 /**
83 * Get current page type
84 *
85 * @since 2.9
86 */
87 public static function page_type() {
88 global $wp_query;
89 $page_type = 'default';
90
91 if ($wp_query->is_page) {
92 $page_type = is_front_page() ? 'front' : 'page';
93 } elseif ($wp_query->is_home) {
94 $page_type = 'home';
95 } elseif ($wp_query->is_single) {
96 // $page_type = $wp_query->is_attachment ? 'attachment' : 'single';
97 $page_type = get_post_type();
98 } elseif ($wp_query->is_category) {
99 $page_type = 'category';
100 } elseif ($wp_query->is_tag) {
101 $page_type = 'tag';
102 } elseif ($wp_query->is_tax) {
103 $page_type = 'tax';
104 // $page_type = get_queried_object()->taxonomy;
105 } elseif ($wp_query->is_archive) {
106 if ($wp_query->is_day) {
107 $page_type = 'day';
108 } elseif ($wp_query->is_month) {
109 $page_type = 'month';
110 } elseif ($wp_query->is_year) {
111 $page_type = 'year';
112 } elseif ($wp_query->is_author) {
113 $page_type = 'author';
114 } else {
115 $page_type = 'archive';
116 }
117 } elseif ($wp_query->is_search) {
118 $page_type = 'search';
119 } elseif ($wp_query->is_404) {
120 $page_type = '404';
121 }
122
123 return $page_type;
124
125 // if ( is_404() ) {
126 // $page_type = '404';
127 // }
128 // elseif ( is_singular() ) {
129 // $page_type = get_post_type();
130 // }
131 // elseif ( is_home() && get_option( 'show_on_front' ) == 'page' ) {
132 // $page_type = 'home';
133 // }
134 // elseif ( is_front_page() ) {
135 // $page_type = 'front';
136 // }
137 // elseif ( is_tax() ) {
138 // $page_type = get_queried_object()->taxonomy;
139 // }
140 // elseif ( is_category() ) {
141 // $page_type = 'category';
142 // }
143 // elseif ( is_tag() ) {
144 // $page_type = 'tag';
145 // }
146
147 // return $page_type;
148 }
149
150 /**
151 * Get ping speed
152 *
153 * @since 2.9
154 */
155 public static function ping( $domain ) {
156 if (strpos($domain, ':')) {
157 $domain = parse_url($domain, PHP_URL_HOST);
158 }
159 $starttime = microtime(true);
160 $file = fsockopen($domain, 443, $errno, $errstr, 10);
161 $stoptime = microtime(true);
162 $status = 0;
163
164 if (!$file) {
165 $status = 99999;
166 }
167 // Site is down
168 else {
169 fclose($file);
170 $status = ($stoptime - $starttime) * 1000;
171 $status = floor($status);
172 }
173
174 Debug2::debug("[Util] ping [Domain] $domain \t[Speed] $status");
175
176 return $status;
177 }
178
179 /**
180 * Set seconds/timestamp to readable format
181 *
182 * @since 1.6.5
183 * @access public
184 */
185 public static function readable_time( $seconds_or_timestamp, $timeout = 3600, $forward = false ) {
186 if (strlen($seconds_or_timestamp) == 10) {
187 $seconds = time() - $seconds_or_timestamp;
188 if ($seconds > $timeout) {
189 return date('m/d/Y H:i:s', $seconds_or_timestamp + LITESPEED_TIME_OFFSET);
190 }
191 } else {
192 $seconds = $seconds_or_timestamp;
193 }
194
195 $res = '';
196 if ($seconds > 86400) {
197 $num = floor($seconds / 86400);
198 $res .= $num . 'd';
199 $seconds %= 86400;
200 }
201
202 if ($seconds > 3600) {
203 if ($res) {
204 $res .= ', ';
205 }
206 $num = floor($seconds / 3600);
207 $res .= $num . 'h';
208 $seconds %= 3600;
209 }
210
211 if ($seconds > 60) {
212 if ($res) {
213 $res .= ', ';
214 }
215 $num = floor($seconds / 60);
216 $res .= $num . 'm';
217 $seconds %= 60;
218 }
219
220 if ($seconds > 0) {
221 if ($res) {
222 $res .= ' ';
223 }
224 $res .= $seconds . 's';
225 }
226
227 if (!$res) {
228 return $forward ? __('right now', 'litespeed-cache') : __('just now', 'litespeed-cache');
229 }
230
231 $res = $forward ? $res : sprintf(__(' %s ago', 'litespeed-cache'), $res);
232
233 return $res;
234 }
235
236 /**
237 * Convert array to string
238 *
239 * @since 1.6
240 * @access public
241 */
242 public static function arr2str( $arr ) {
243 if (!is_array($arr)) {
244 return $arr;
245 }
246
247 return base64_encode(\json_encode($arr));
248 }
249
250 /**
251 * Get human readable size
252 *
253 * @since 1.6
254 * @access public
255 */
256 public static function real_size( $filesize, $is_1000 = false ) {
257 $unit = $is_1000 ? 1000 : 1024;
258
259 if ($filesize >= pow($unit, 3)) {
260 $filesize = round(($filesize / pow($unit, 3)) * 100) / 100 . 'G';
261 } elseif ($filesize >= pow($unit, 2)) {
262 $filesize = round(($filesize / pow($unit, 2)) * 100) / 100 . 'M';
263 } elseif ($filesize >= $unit) {
264 $filesize = round(($filesize / $unit) * 100) / 100 . 'K';
265 } else {
266 $filesize = $filesize . 'B';
267 }
268 return $filesize;
269 }
270
271 /**
272 * Parse attributes from string
273 *
274 * @since 1.2.2
275 * @since 1.4 Moved from optimize to utility
276 * @access private
277 * @param string $str
278 * @return array All the attributes
279 */
280 public static function parse_attr( $str ) {
281 $attrs = array();
282 preg_match_all('#([\w-]+)=(["\'])([^\2]*)\2#isU', $str, $matches, PREG_SET_ORDER);
283 foreach ($matches as $match) {
284 $attrs[$match[1]] = trim($match[3]);
285 }
286 return $attrs;
287 }
288
289 /**
290 * Check if an array has a string
291 *
292 * Support $ exact match
293 *
294 * @since 1.3
295 * @access private
296 * @param string $needle The string to search with
297 * @param array $haystack
298 * @return bool|string False if not found, otherwise return the matched string in haystack.
299 */
300 public static function str_hit_array( $needle, $haystack, $has_ttl = false ) {
301 if (!$haystack) {
302 return false;
303 }
304 /**
305 * Safety check to avoid PHP warning
306 *
307 * @see https://github.com/litespeedtech/lscache_wp/pull/131/commits/45fc03af308c7d6b5583d1664fad68f75fb6d017
308 */
309 if (!is_array($haystack)) {
310 Debug2::debug('[Util] ❌ bad param in str_hit_array()!');
311
312 return false;
313 }
314
315 $hit = false;
316 $this_ttl = 0;
317 foreach ($haystack as $item) {
318 if (!$item) {
319 continue;
320 }
321
322 if ($has_ttl) {
323 $this_ttl = 0;
324 $item = explode(' ', $item);
325 if (!empty($item[1])) {
326 $this_ttl = $item[1];
327 }
328 $item = $item[0];
329 }
330
331 if (substr($item, 0, 1) === '^' && substr($item, -1) === '$') {
332 // do exact match
333 if (substr($item, 1, -1) === $needle) {
334 $hit = $item;
335 break;
336 }
337 } elseif (substr($item, -1) === '$') {
338 // match end
339 if (substr($item, 0, -1) === substr($needle, -strlen($item) + 1)) {
340 $hit = $item;
341 break;
342 }
343 } elseif (substr($item, 0, 1) === '^') {
344 // match beginning
345 if (substr($item, 1) === substr($needle, 0, strlen($item) - 1)) {
346 $hit = $item;
347 break;
348 }
349 } elseif (strpos($needle, $item) !== false) {
350 $hit = $item;
351 break;
352 }
353 }
354
355 if ($hit) {
356 if ($has_ttl) {
357 return array( $hit, $this_ttl );
358 }
359
360 return $hit;
361 }
362
363 return false;
364 }
365
366 /**
367 * Improve compatibility to PHP old versions
368 *
369 * @since 1.2.2
370 */
371 public static function compatibility() {
372 require_once LSCWP_DIR . 'lib/php-compatibility.func.php';
373 }
374
375 /**
376 * Convert URI to URL
377 *
378 * @since 1.3
379 * @access public
380 * @param string $uri `xx/xx.html` or `/subfolder/xx/xx.html`
381 * @return string http://www.example.com/subfolder/xx/xx.html
382 */
383 public static function uri2url( $uri ) {
384 if (substr($uri, 0, 1) === '/') {
385 self::domain_const();
386 $url = LSCWP_DOMAIN . $uri;
387 } else {
388 $url = home_url('/') . $uri;
389 }
390
391 return $url;
392 }
393
394 /**
395 * Convert URL to basename (filename)
396 *
397 * @since 4.7
398 */
399 public static function basename( $url ) {
400 $url = trim($url);
401 $uri = @parse_url($url, PHP_URL_PATH);
402 $basename = pathinfo($uri, PATHINFO_BASENAME);
403
404 return $basename;
405 }
406
407 /**
408 * Drop .webp and .avif if existed in filename
409 *
410 * @since 4.7
411 */
412 public static function drop_webp( $filename ) {
413 if (in_array(substr($filename, -5), array( '.webp', '.avif' ))) {
414 $filename = substr($filename, 0, -5);
415 }
416
417 return $filename;
418 }
419
420 /**
421 * Convert URL to URI
422 *
423 * @since 1.2.2
424 * @since 1.6.2.1 Added 2nd param keep_qs
425 * @access public
426 */
427 public static function url2uri( $url, $keep_qs = false ) {
428 $url = trim($url);
429 $uri = @parse_url($url, PHP_URL_PATH);
430 $qs = @parse_url($url, PHP_URL_QUERY);
431
432 if (!$keep_qs || !$qs) {
433 return $uri;
434 }
435
436 return $uri . '?' . $qs;
437 }
438
439 /**
440 * Get attachment relative path to upload folder
441 *
442 * @since 3.0
443 * @access public
444 * @param string $url `https://aa.com/bbb/wp-content/upload/2018/08/test.jpg` or `/bbb/wp-content/upload/2018/08/test.jpg`
445 * @return string `2018/08/test.jpg`
446 */
447 public static function att_short_path( $url ) {
448 if (!defined('LITESPEED_UPLOAD_PATH')) {
449 $_wp_upload_dir = wp_upload_dir();
450
451 $upload_path = self::url2uri($_wp_upload_dir['baseurl']);
452
453 define('LITESPEED_UPLOAD_PATH', $upload_path);
454 }
455
456 $local_file = self::url2uri($url);
457
458 $short_path = substr($local_file, strlen(LITESPEED_UPLOAD_PATH) + 1);
459
460 return $short_path;
461 }
462
463 /**
464 * Make URL to be relative
465 *
466 * NOTE: for subfolder home_url, will keep subfolder part (strip nothing but scheme and host)
467 *
468 * @param string $url
469 * @return string Relative URL, start with /
470 */
471 public static function make_relative( $url ) {
472 // replace home_url if the url is full url
473 self::domain_const();
474 if (strpos($url, LSCWP_DOMAIN) === 0) {
475 $url = substr($url, strlen(LSCWP_DOMAIN));
476 }
477 return trim($url);
478 }
479
480 /**
481 * Convert URL to domain only
482 *
483 * @since 1.7.1
484 */
485 public static function parse_domain( $url ) {
486 $url = @parse_url($url);
487 if (empty($url['host'])) {
488 return '';
489 }
490
491 if (!empty($url['scheme'])) {
492 return $url['scheme'] . '://' . $url['host'];
493 }
494
495 return '//' . $url['host'];
496 }
497
498 /**
499 * Drop protocol `https:` from https://example.com
500 *
501 * @since 3.3
502 */
503 public static function noprotocol( $url ) {
504 $tmp = parse_url(trim($url));
505 if (!empty($tmp['scheme'])) {
506 $url = str_replace($tmp['scheme'] . ':', '', $url);
507 }
508
509 return $url;
510 }
511
512 /**
513 * Validate ip v4
514 *
515 * @since 5.5
516 */
517 public static function valid_ipv4( $ip ) {
518 return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
519 }
520
521 /**
522 * Generate domain const
523 *
524 * This will generate http://www.example.com even there is a subfolder in home_url setting
525 *
526 * Conf LSCWP_DOMAIN has NO trailing /
527 *
528 * @since 1.3
529 * @access public
530 */
531 public static function domain_const() {
532 if (defined('LSCWP_DOMAIN')) {
533 return;
534 }
535
536 self::compatibility();
537 $domain = http_build_url(get_home_url(), array(), HTTP_URL_STRIP_ALL);
538
539 define('LSCWP_DOMAIN', $domain);
540 }
541
542 /**
543 * Array map one textarea to sanitize the url
544 *
545 * @since 1.3
546 * @access public
547 * @param array|string $arr
548 * @param string|null $type String handler type
549 * @return string|array
550 */
551 public static function sanitize_lines( $arr, $type = null ) {
552 $types = $type ? explode(',', $type) : array();
553
554 if (!$arr) {
555 if ($type === 'string') {
556 return '';
557 }
558 return array();
559 }
560
561 if (!is_array($arr)) {
562 $arr = explode("\n", $arr);
563 }
564
565 $arr = array_map('trim', $arr);
566 $changed = false;
567 if (in_array('uri', $types)) {
568 $arr = array_map(__CLASS__ . '::url2uri', $arr);
569 $changed = true;
570 }
571 if (in_array('basename', $types)) {
572 $arr = array_map(__CLASS__ . '::basename', $arr);
573 $changed = true;
574 }
575 if (in_array('drop_webp', $types)) {
576 $arr = array_map(__CLASS__ . '::drop_webp', $arr);
577 $changed = true;
578 }
579 if (in_array('relative', $types)) {
580 $arr = array_map(__CLASS__ . '::make_relative', $arr); // Remove domain
581 $changed = true;
582 }
583 if (in_array('domain', $types)) {
584 $arr = array_map(__CLASS__ . '::parse_domain', $arr); // Only keep domain
585 $changed = true;
586 }
587
588 if (in_array('noprotocol', $types)) {
589 $arr = array_map(__CLASS__ . '::noprotocol', $arr); // Drop protocol, `https://example.com` -> `//example.com`
590 $changed = true;
591 }
592
593 if (in_array('trailingslash', $types)) {
594 $arr = array_map('trailingslashit', $arr); // Append trailing slash, `https://example.com` -> `https://example.com/`
595 $changed = true;
596 }
597
598 if ($changed) {
599 $arr = array_map('trim', $arr);
600 }
601 $arr = array_unique($arr);
602 $arr = array_filter($arr);
603
604 if (in_array('string', $types)) {
605 return implode("\n", $arr);
606 }
607
608 return $arr;
609 }
610
611 /**
612 * Builds an url with an action and a nonce.
613 *
614 * Assumes user capabilities are already checked.
615 *
616 * @since 1.6 Changed order of 2nd&3rd param, changed 3rd param `append_str` to 2nd `type`
617 * @access public
618 * @return string The built url.
619 */
620 public static function build_url( $action, $type = false, $is_ajax = false, $page = null, $append_arr = array(), $unescape = false ) {
621 $prefix = '?';
622
623 if ($page === '_ori') {
624 $page = true;
625 $append_arr['_litespeed_ori'] = 1;
626 }
627
628 if (!$is_ajax) {
629 if ($page) {
630 // If use admin url
631 if ($page === true) {
632 $page = 'admin.php';
633 } elseif (strpos($page, '?') !== false) {
634 $prefix = '&';
635 }
636 $combined = $page . $prefix . Router::ACTION . '=' . $action;
637 } else {
638 // Current page rebuild URL
639 $params = $_GET;
640
641 if (!empty($params)) {
642 if (isset($params[Router::ACTION])) {
643 unset($params[Router::ACTION]);
644 }
645 if (isset($params['_wpnonce'])) {
646 unset($params['_wpnonce']);
647 }
648 if (!empty($params)) {
649 $prefix .= http_build_query($params) . '&';
650 }
651 }
652 global $pagenow;
653 $combined = $pagenow . $prefix . Router::ACTION . '=' . $action;
654 }
655 } else {
656 $combined = 'admin-ajax.php?action=litespeed_ajax&' . Router::ACTION . '=' . $action;
657 }
658
659 if (is_network_admin()) {
660 $prenonce = network_admin_url($combined);
661 } else {
662 $prenonce = admin_url($combined);
663 }
664 $url = wp_nonce_url($prenonce, $action, Router::NONCE);
665
666 if ($type) {
667 // Remove potential param `type` from url
668 $url = parse_url(htmlspecialchars_decode($url));
669 parse_str($url['query'], $query);
670
671 $built_arr = array_merge($query, array( Router::TYPE => $type ));
672 if ($append_arr) {
673 $built_arr = array_merge($built_arr, $append_arr);
674 }
675 $url['query'] = http_build_query($built_arr);
676 self::compatibility();
677 $url = http_build_url($url);
678 $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
679 }
680
681 if ($unescape) {
682 $url = wp_specialchars_decode($url);
683 }
684
685 return $url;
686 }
687
688 /**
689 * Check if the host is the internal host
690 *
691 * @since 1.2.3
692 */
693 public static function internal( $host ) {
694 if (!defined('LITESPEED_FRONTEND_HOST')) {
695 if (defined('WP_HOME')) {
696 $home_host = constant('WP_HOME'); // Also think of `WP_SITEURL`
697 } else {
698 $home_host = get_option('home');
699 }
700 define('LITESPEED_FRONTEND_HOST', parse_url($home_host, PHP_URL_HOST));
701 }
702
703 if ($host === LITESPEED_FRONTEND_HOST) {
704 return true;
705 }
706
707 /**
708 * Filter for multiple domains
709 *
710 * @since 2.9.4
711 */
712 if (!isset(self::$_internal_domains)) {
713 self::$_internal_domains = apply_filters('litespeed_internal_domains', array());
714 }
715
716 if (self::$_internal_domains) {
717 return in_array($host, self::$_internal_domains);
718 }
719
720 return false;
721 }
722
723 /**
724 * Check if an URL is a internal existing file
725 *
726 * @since 1.2.2
727 * @since 1.6.2 Moved here from optm.cls due to usage of media.cls
728 * @access public
729 * @return string|bool The real path of file OR false
730 */
731 public static function is_internal_file( $url, $addition_postfix = false ) {
732 if (substr($url, 0, 5) == 'data:') {
733 Debug2::debug2('[Util] data: content not file');
734 return false;
735 }
736 $url_parsed = parse_url($url);
737 if (isset($url_parsed['host']) && !self::internal($url_parsed['host'])) {
738 // Check if is cdn path
739 // Do this to avoid user hardcoded src in tpl
740 if (!CDN::internal($url_parsed['host'])) {
741 Debug2::debug2('[Util] external');
742 return false;
743 }
744 }
745
746 if (empty($url_parsed['path'])) {
747 return false;
748 }
749
750 // Need to replace child blog path for assets, ref: .htaccess
751 if (is_multisite() && defined('PATH_CURRENT_SITE')) {
752 $pattern = '#^' . PATH_CURRENT_SITE . '([_0-9a-zA-Z-]+/)(wp-(content|admin|includes))#U';
753 $replacement = PATH_CURRENT_SITE . '$2';
754 $url_parsed['path'] = preg_replace($pattern, $replacement, $url_parsed['path']);
755 // $current_blog = (int) get_current_blog_id();
756 // $main_blog_id = (int) get_network()->site_id;
757 // if ( $current_blog === $main_blog_id ) {
758 // define( 'LITESPEED_IS_MAIN_BLOG', true );
759 // }
760 // else {
761 // define( 'LITESPEED_IS_MAIN_BLOG', false );
762 // }
763 }
764
765 // Parse file path
766 /**
767 * Trying to fix pure /.htaccess rewrite to /wordpress case
768 *
769 * Add `define( 'LITESPEED_WP_REALPATH', '/wordpress' );` in wp-config.php in this case
770 *
771 * @internal #611001 - Combine & Minify not working?
772 * @since 1.6.3
773 */
774 if (substr($url_parsed['path'], 0, 1) === '/') {
775 if (defined('LITESPEED_WP_REALPATH')) {
776 $file_path_ori = $_SERVER['DOCUMENT_ROOT'] . constant('LITESPEED_WP_REALPATH') . $url_parsed['path'];
777 } else {
778 $file_path_ori = $_SERVER['DOCUMENT_ROOT'] . $url_parsed['path'];
779 }
780 } else {
781 $file_path_ori = Router::frontend_path() . '/' . $url_parsed['path'];
782 }
783
784 /**
785 * Added new file postfix to be check if passed in
786 *
787 * @since 2.2.4
788 */
789 if ($addition_postfix) {
790 $file_path_ori .= '.' . $addition_postfix;
791 }
792
793 /**
794 * Added this filter for those plugins which overwrite the filepath
795 *
796 * @see #101091 plugin `Hide My WordPress`
797 * @since 2.2.3
798 */
799 $file_path_ori = apply_filters('litespeed_realpath', $file_path_ori);
800
801 $file_path = realpath($file_path_ori);
802 if (!is_file($file_path)) {
803 Debug2::debug2('[Util] file not exist: ' . $file_path_ori);
804 return false;
805 }
806
807 return array( $file_path, filesize($file_path) );
808 }
809
810 /**
811 * Safely parse URL for v5.3 compatibility
812 *
813 * @since 3.4.3
814 */
815 public static function parse_url_safe( $url, $component = -1 ) {
816 if (substr($url, 0, 2) == '//') {
817 $url = 'https:' . $url;
818 }
819
820 return parse_url($url, $component);
821 }
822
823 /**
824 * Replace url in srcset to new value
825 *
826 * @since 2.2.3
827 */
828 public static function srcset_replace( $content, $callback ) {
829 preg_match_all('# srcset=([\'"])(.+)\g{1}#iU', $content, $matches);
830 $srcset_ori = array();
831 $srcset_final = array();
832 foreach ($matches[2] as $k => $urls_ori) {
833 $urls_final = explode(',', $urls_ori);
834
835 $changed = false;
836
837 foreach ($urls_final as $k2 => $url_info) {
838 $url_info_arr = explode(' ', trim($url_info));
839
840 if (!($url2 = call_user_func($callback, $url_info_arr[0]))) {
841 continue;
842 }
843
844 $changed = true;
845
846 $urls_final[$k2] = str_replace($url_info_arr[0], $url2, $url_info);
847
848 Debug2::debug2('[Util] - srcset replaced to ' . $url2 . (!empty($url_info_arr[1]) ? ' ' . $url_info_arr[1] : ''));
849 }
850
851 if (!$changed) {
852 continue;
853 }
854
855 $urls_final = implode(',', $urls_final);
856
857 $srcset_ori[] = $matches[0][$k];
858
859 $srcset_final[] = str_replace($urls_ori, $urls_final, $matches[0][$k]);
860 }
861
862 if ($srcset_ori) {
863 $content = str_replace($srcset_ori, $srcset_final, $content);
864 Debug2::debug2('[Util] - srcset replaced');
865 }
866
867 return $content;
868 }
869
870 /**
871 * Generate pagination
872 *
873 * @since 3.0
874 * @access public
875 */
876 public static function pagination( $total, $limit, $return_offset = false ) {
877 $pagenum = isset($_GET['pagenum']) ? absint($_GET['pagenum']) : 1;
878
879 $offset = ($pagenum - 1) * $limit;
880 $num_of_pages = ceil($total / $limit);
881
882 if ($offset > $total) {
883 $offset = $total - $limit;
884 }
885
886 if ($offset < 0) {
887 $offset = 0;
888 }
889
890 if ($return_offset) {
891 return $offset;
892 }
893
894 $page_links = paginate_links(array(
895 'base' => add_query_arg('pagenum', '%#%'),
896 'format' => '',
897 'prev_text' => '&laquo;',
898 'next_text' => '&raquo;',
899 'total' => $num_of_pages,
900 'current' => $pagenum,
901 ));
902
903 return '<div class="tablenav"><div class="tablenav-pages" style="margin: 1em 0">' . $page_links . '</div></div>';
904 }
905
906 /**
907 * Generate placeholder for an array to query
908 *
909 * @since 2.0
910 * @access public
911 */
912 public static function chunk_placeholder( $data, $fields ) {
913 $division = substr_count($fields, ',') + 1;
914
915 $q = implode(
916 ',',
917 array_map(function ( $el ) {
918 return '(' . implode(',', $el) . ')';
919 }, array_chunk(array_fill(0, count($data), '%s'), $division))
920 );
921
922 return $q;
923 }
924
925 /**
926 * Prepare image sizes for optimization.
927 *
928 * @since 7.5
929 * @access public
930 */
931 public static function prepare_image_sizes_array( $detailed = false ) {
932 $image_sizes = wp_get_registered_image_subsizes();
933 $sizes = [];
934
935 foreach ( $image_sizes as $current_size_name => $current_size ) {
936 if( empty( $current_size['width'] ) && empty( $current_size['height'] ) ) continue;
937
938 if( !$detailed ) {
939 $sizes[] = $current_size_name;
940 }
941 else{
942 $label = $current_size['width'] . 'x' . $current_size['height'];
943 if( $current_size_name !== $label ){
944 $label = ucfirst( $current_size_name ) . ' ( ' . $label . ' )';
945 }
946
947 $sizes[] = [
948 "label" => $label,
949 "file_size" => $current_size_name,
950 "width" => $current_size['width'],
951 "height" => $current_size['height'],
952 ];
953 }
954 }
955
956 return $sizes;
957 }
958 }
959