* @since 1.5 Moved into /inc
defined('WPINC') || exit();
class Utility extends Root
private static $_internal_domains;
* @since 3.0 Moved here from admin-settings.cls
* @return bool True for valid rules, false otherwise.
public static function syntax_checker($rules)
return preg_match(self::arr2regex($rules), '') !== false;
* Combine regex array to regex rule
public static function arr2regex($arr, $drop_delimiter = false)
$arr = self::sanitize_lines($arr);
$new_arr[] = preg_quote($v, '#');
$regex = implode('|', $new_arr);
$regex = str_replace(' ', '\\ ', $regex);
return '#' . $regex . '#';
* Replace wildcard to regex
public static function wildcard2regex($string)
return array_map(__CLASS__ . '::wildcard2regex', $string);
if (strpos($string, '*') !== false) {
$string = preg_quote($string, '#');
$string = str_replace('\*', '.*', $string);
* Check if an URL or current page is REST req or not
* @deprecated 2.9.4 Moved to REST class
public static function is_rest($url = false)
public static function page_type()
if ($wp_query->is_page) {
$page_type = is_front_page() ? 'front' : 'page';
} elseif ($wp_query->is_home) {
} elseif ($wp_query->is_single) {
// $page_type = $wp_query->is_attachment ? 'attachment' : 'single';
$page_type = get_post_type();
} elseif ($wp_query->is_category) {
} elseif ($wp_query->is_tag) {
} elseif ($wp_query->is_tax) {
// $page_type = get_queried_object()->taxonomy;
} elseif ($wp_query->is_archive) {
} elseif ($wp_query->is_month) {
} elseif ($wp_query->is_year) {
} elseif ($wp_query->is_author) {
} elseif ($wp_query->is_search) {
} elseif ($wp_query->is_404) {
// elseif ( is_singular() ) {
// $page_type = get_post_type();
// elseif ( is_home() && get_option( 'show_on_front' ) == 'page' ) {
// elseif ( is_front_page() ) {
// $page_type = get_queried_object()->taxonomy;
// elseif ( is_category() ) {
// $page_type = 'category';
public static function ping($domain)
if (strpos($domain, ':')) {
$domain = parse_url($domain, PHP_URL_HOST);
$starttime = microtime(true);
$file = fsockopen($domain, 443, $errno, $errstr, 10);
$stoptime = microtime(true);
$status = ($stoptime - $starttime) * 1000;
$status = floor($status);
Debug2::debug("[Util] ping [Domain] $domain \t[Speed] $status");
* Set seconds/timestamp to readable format
public static function readable_time($seconds_or_timestamp, $timeout = 3600, $forward = false)
if (strlen($seconds_or_timestamp) == 10) {
$seconds = time() - $seconds_or_timestamp;
if ($seconds > $timeout) {
return date('m/d/Y H:i:s', $seconds_or_timestamp + LITESPEED_TIME_OFFSET);
$seconds = $seconds_or_timestamp;
$num = floor($seconds / 86400);
$num = floor($seconds / 3600);
$num = floor($seconds / 60);
return $forward ? __('right now', 'litespeed-cache') : __('just now', 'litespeed-cache');
$res = $forward ? $res : sprintf(__(' %s ago', 'litespeed-cache'), $res);
* Convert array to string
public static function arr2str($arr)
return base64_encode(\json_encode($arr));
* Get human readable size
public static function real_size($filesize, $is_1000 = false)
$unit = $is_1000 ? 1000 : 1024;
if ($filesize >= pow($unit, 3)) {
$filesize = round(($filesize / pow($unit, 3)) * 100) / 100 . 'G';
} elseif ($filesize >= pow($unit, 2)) {
$filesize = round(($filesize / pow($unit, 2)) * 100) / 100 . 'M';
} elseif ($filesize >= $unit) {
$filesize = round(($filesize / $unit) * 100) / 100 . 'K';
$filesize = $filesize . 'B';
* Parse attributes from string
* @since 1.4 Moved from optimize to utility
* @return array All the attributes
public static function parse_attr($str)
preg_match_all('#([\w-]+)=(["\'])([^\2]*)\2#isU', $str, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$attrs[$match[1]] = trim($match[3]);
* Check if an array has a string
* @param string $needle The string to search with
* @return bool|string False if not found, otherwise return the matched string in haystack.
public static function str_hit_array($needle, $haystack, $has_ttl = false)
* Safety check to avoid PHP warning
* @see https://github.com/litespeedtech/lscache_wp/pull/131/commits/45fc03af308c7d6b5583d1664fad68f75fb6d017
if (!is_array($haystack)) {
Debug2::debug('[Util] ❌ bad param in str_hit_array()!');
foreach ($haystack as $item) {
$item = explode(' ', $item);
if (substr($item, 0, 1) === '^' && substr($item, -1) === '$') {
if (substr($item, 1, -1) === $needle) {
} elseif (substr($item, -1) === '$') {
if (substr($item, 0, -1) === substr($needle, -strlen($item) + 1)) {
} elseif (substr($item, 0, 1) === '^') {
if (substr($item, 1) === substr($needle, 0, strlen($item) - 1)) {
if (strpos($needle, $item) !== false) {
return array($hit, $this_ttl);
* Improve compatibility to PHP old versions
public static function compatibility()
require_once LSCWP_DIR . 'lib/php-compatibility.func.php';
* @param string $uri `xx/xx.html` or `/subfolder/xx/xx.html`
* @return string http://www.example.com/subfolder/xx/xx.html
public static function uri2url($uri)
if (substr($uri, 0, 1) === '/') {
$url = LSCWP_DOMAIN . $uri;
$url = home_url('/') . $uri;
* Convert URL to basename (filename)
public static function basename($url)
$uri = @parse_url($url, PHP_URL_PATH);
$basename = pathinfo($uri, PATHINFO_BASENAME);
* Drop .webp if existed in filename
public static function drop_webp($filename)
if (substr($filename, -5) === '.webp') {
$filename = substr($filename, 0, -5);
* @since 1.6.2.1 Added 2nd param keep_qs
public static function url2uri($url, $keep_qs = false)
$uri = @parse_url($url, PHP_URL_PATH);
$qs = @parse_url($url, PHP_URL_QUERY);
* Get attachment relative path to upload folder
* @param string `https://aa.com/bbb/wp-content/upload/2018/08/test.jpg` or `/bbb/wp-content/upload/2018/08/test.jpg`
* @return string `2018/08/test.jpg`
public static function att_short_path($url)
if (!defined('LITESPEED_UPLOAD_PATH')) {
$_wp_upload_dir = wp_upload_dir();
$upload_path = self::url2uri($_wp_upload_dir['baseurl']);
define('LITESPEED_UPLOAD_PATH', $upload_path);
$local_file = self::url2uri($url);
$short_path = substr($local_file, strlen(LITESPEED_UPLOAD_PATH) + 1);
* Make URL to be relative
* NOTE: for subfolder home_url, will keep subfolder part (strip nothing but scheme and host)
* @return string Relative URL, start with /
public static function make_relative($url)
// replace home_url if the url is full url
if (strpos($url, LSCWP_DOMAIN) === 0) {
$url = substr($url, strlen(LSCWP_DOMAIN));