self::O_MEDIA_LAZY => false,
self::O_MEDIA_LAZY_PLACEHOLDER => '',
self::O_MEDIA_PLACEHOLDER_RESP => false,
self::O_MEDIA_PLACEHOLDER_RESP_COLOR => '',
self::O_MEDIA_PLACEHOLDER_RESP_SVG => '',
self::O_MEDIA_LQIP => false,
self::O_MEDIA_LQIP_QUAL => 0,
self::O_MEDIA_LQIP_MIN_W => 0,
self::O_MEDIA_LQIP_MIN_H => 0,
self::O_MEDIA_PLACEHOLDER_RESP_ASYNC => false,
self::O_MEDIA_IFRAME_LAZY => false,
self::O_MEDIA_ADD_MISSING_SIZES => false,
self::O_MEDIA_LAZY_EXC => [],
self::O_MEDIA_LAZY_CLS_EXC => [],
self::O_MEDIA_LAZY_PARENT_CLS_EXC => [],
self::O_MEDIA_IFRAME_LAZY_CLS_EXC => [],
self::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC => [],
self::O_MEDIA_LAZY_URI_EXC => [],
self::O_MEDIA_LQIP_EXC => [],
self::O_MEDIA_VPI => false,
self::O_MEDIA_VPI_CRON => false,
self::O_MEDIA_AUTO_RESCALE_ORI => false,
self::O_IMG_OPTM_AUTO => false,
self::O_IMG_OPTM_ORI => false,
self::O_IMG_OPTM_RM_BKUP => false,
self::O_IMG_OPTM_WEBP => false,
self::O_IMG_OPTM_LOSSLESS => false,
self::O_IMG_OPTM_SIZES_SKIPPED => [],
self::O_IMG_OPTM_EXIF => false,
self::O_IMG_OPTM_WEBP_ATTR => [],
self::O_IMG_OPTM_WEBP_REPLACE_SRCSET => false,
self::O_IMG_OPTM_JPG_QUALITY => 0,
self::O_CRAWLER => false,
self::O_CRAWLER_CRAWL_INTERVAL => 0,
self::O_CRAWLER_LOAD_LIMIT => 0,
self::O_CRAWLER_SITEMAP => '',
self::O_CRAWLER_ROLES => [],
self::O_CRAWLER_COOKIES => [],
self::O_MISC_HEARTBEAT_FRONT => false,
self::O_MISC_HEARTBEAT_FRONT_TTL => 0,
self::O_MISC_HEARTBEAT_BACK => false,
self::O_MISC_HEARTBEAT_BACK_TTL => 0,
self::O_MISC_HEARTBEAT_EDITOR => false,
self::O_MISC_HEARTBEAT_EDITOR_TTL => 0,
self::O_CDN_ORI_DIR => [],
self::O_CDN_QUIC => false,
self::O_CDN_CLOUDFLARE => false,
self::O_CDN_CLOUDFLARE_EMAIL => '',
self::O_CDN_CLOUDFLARE_KEY => '',
self::O_CDN_CLOUDFLARE_NAME => '',
self::O_CDN_CLOUDFLARE_ZONE => '',
self::O_CDN_CLOUDFLARE_CLEAR => false,
self::O_CDN_MAPPING => [],
self::O_QC_NAMESERVERS => '',
self::DEBUG_TMP_DISABLE => 0,
* Default options for multisite (site-level options stored network-wide).
* @var array<string,mixed>
protected static $_default_site_options = [
self::NETWORK_O_USE_PRIMARY => false,
self::O_AUTO_UPGRADE => false,
self::O_CACHE_BROWSER => false,
self::O_CACHE_MOBILE => false,
self::O_CACHE_MOBILE_RULES => [],
self::O_CACHE_DROP_QS => [],
self::O_CACHE_LOGIN_COOKIE => '',
self::O_CACHE_VARY_COOKIES => [],
self::O_CACHE_EXC_COOKIES => [],
self::O_CACHE_EXC_USERAGENTS => [],
self::O_CACHE_TTL_BROWSER => 0,
self::O_PURGE_ON_UPGRADE => false,
self::O_OBJECT_KIND => false,
self::O_OBJECT_HOST => '',
self::O_OBJECT_PORT => 0,
self::O_OBJECT_LIFE => 0,
self::O_OBJECT_PERSISTENT => false,
self::O_OBJECT_ADMIN => false,
self::O_OBJECT_DB_ID => 0,
self::O_OBJECT_USER => '',
self::O_OBJECT_PSWD => '',
self::O_OBJECT_GLOBAL_GROUPS => [],
self::O_OBJECT_NON_PERSISTENT_GROUPS => [],
self::O_DEBUG_DISABLE_ALL => false,
self::O_DEBUG_LEVEL => false,
self::O_DEBUG_FILESIZE => 0,
self::O_DEBUG_COLLAPSE_QS => false,
self::O_DEBUG_EXC_STRINGS => [],
self::O_IMG_OPTM_WEBP => false,
* Multi-switch options: option ID => max state (int).
* NOTE: all the val of following items will be int while not bool
protected static $_multi_switch_list = [
self::O_OPTM_JS_DEFER => 2,
self::O_IMG_OPTM_WEBP => 2,
* Cache for blog options to avoid repeated switch_to_blog calls.
* Structure: [ blog_id => [ option_name => value, ... ], ... ]
* @var array<int,array<string,mixed>>
private static $_blog_options_cache = [];
* Get option from another blog with batch preload optimization.
* Reduces N*2 switch_to_blog calls to just 2 by preloading all options on first call.
* @param int $blog_id Blog ID to get option from.
* @param string $id Option ID (without prefix).
* @param mixed $default_v Default value if option not found.
* @return mixed Option value.
public static function get_blog_option( $blog_id, $id, $default_v = false ) {
$blog_id = (int) $blog_id;
// If current blog, use get_option directly (no switch needed).
if ( get_current_blog_id() === $blog_id ) {
return self::get_option( $id, $default_v );
// Preload all options for this blog if not cached.
if ( ! isset( self::$_blog_options_cache[ $blog_id ] ) ) {
self::_preload_blog_options( $blog_id );
$v = self::$_blog_options_cache[ $blog_id ][ $id ];
if ( is_array( $default_v ) ) {
$v = self::_maybe_decode( $v );
* Preload all conf options for a blog into cache.
* @param int $blog_id Blog ID to preload options from.
private static function _preload_blog_options( $blog_id ) {
self::$_blog_options_cache[ $blog_id ] = [];
switch_to_blog( $blog_id );
foreach ( self::$_default_options as $id => $default_v ) {
self::$_blog_options_cache[ $blog_id ][ $id ] = get_option( self::name( $id ), $default_v );
* Correct the option type.
* TODO: add similar network func
* @param mixed $val Incoming value.
* @param string $id Option ID.
* @param bool $is_site_conf Whether using site-level defaults.
protected function type_casting( $val, $id, $is_site_conf = false ) {
$default_v = ! $is_site_conf ? self::$_default_options[ $id ] : self::$_default_site_options[ $id ];
if ( is_bool( $default_v ) ) {
if ( 'false' === $val ) {
$max = $this->_conf_multi_switch( $id );
} elseif ( is_array( $default_v ) ) {
if ( ! is_array( $val ) ) {
$val = Utility::sanitize_lines( $val, $this->_conf_filter( $id ) );
} elseif ( ! is_string( $default_v ) ) {
// Check if the string has a limit set
$val = $this->_conf_string_val( $id, $val );
* Load default network settings from data.ini
* @return array<string,mixed>
public function load_default_site_vals() {
// Load network_default.json
if ( file_exists( LSCWP_DIR . 'data/const.network_default.json' ) ) {
$default_ini_cfg = json_decode( File::read( LSCWP_DIR . 'data/const.network_default.json' ), true );
foreach ( self::$_default_site_options as $k => $v ) {
if ( ! array_key_exists( $k, $default_ini_cfg ) ) {
// Parse value in ini file
$ini_v = $this->type_casting( $default_ini_cfg[ $k ], $k, true );
self::$_default_site_options[ $k ] = $ini_v;
self::$_default_site_options[ self::_VER ] = Core::VER;
return self::$_default_site_options;
* Load default values from default.json
* @return array<string,mixed>
public function load_default_vals() {
if ( file_exists( LSCWP_DIR . 'data/const.default.json' ) ) {
$default_ini_cfg = json_decode( File::read( LSCWP_DIR . 'data/const.default.json' ), true );
foreach ( self::$_default_options as $k => $v ) {
if ( ! array_key_exists( $k, $default_ini_cfg ) ) {
// Parse value in ini file
$ini_v = $this->type_casting( $default_ini_cfg[ $k ], $k );
// NOTE: Multiple lines value must be stored as array
* Special handler for CDN_mapping
* url[0] = 'https://example.com/'
* [0] = [ 'url' => 'https://example.com', 'inc_js' => true, 'filetype' => [ '.css', '.js', '.jpg' ] ]
if ( self::O_CDN_MAPPING === $k ) {
self::CDN_MAPPING_INC_IMG,
self::CDN_MAPPING_INC_CSS,
self::CDN_MAPPING_INC_JS,
self::CDN_MAPPING_FILETYPE, // Array
foreach ( $ini_v[ self::CDN_MAPPING_URL ] as $k2 => $v2 ) {
foreach ( $mapping_fields as $v3 ) {
$this_v = ! empty( $ini_v[ $v3 ][ $k2 ] ) ? $ini_v[ $v3 ][ $k2 ] : false;
if ( self::CDN_MAPPING_URL === $v3 ) {
if ( empty( $this_v ) ) {
if ( self::CDN_MAPPING_FILETYPE === $v3 ) {
$this_v = $this_v ? Utility::sanitize_lines( $this_v ) : []; // Note: Since v3.0 its already an array
$this_row[ $v3 ] = $this_v;
$ini_v2[ $k2 ] = $this_row;
self::$_default_options[ $k ] = $ini_v;
// Load internal default vals
// Setting the default bool to int is also to avoid type casting override it back to bool
self::$_default_options[ self::O_CACHE ] = is_multisite() ? self::VAL_ON2 : self::VAL_ON; // For multi site, default is 2 (Use Network Admin Settings). For single site, default is 1 (Enabled).
// Load default vals containing variables
if ( ! self::$_default_options[ self::O_CDN_ORI_DIR ] ) {
self::$_default_options[ self::O_CDN_ORI_DIR ] = LSCWP_CONTENT_FOLDER . "\nwp-includes";
self::$_default_options[ self::O_CDN_ORI_DIR ] = explode( "\n", self::$_default_options[ self::O_CDN_ORI_DIR ] );
self::$_default_options[ self::O_CDN_ORI_DIR ] = array_map( 'trim', self::$_default_options[ self::O_CDN_ORI_DIR ] );
// Set security key if not initialized yet
if ( ! self::$_default_options[ self::HASH ] ) {
self::$_default_options[ self::HASH ] = Str::rrand( 32 );
self::$_default_options[ self::_VER ] = Core::VER;
return self::$_default_options;
* Format the string value.
* @param string $id Option ID.
* @param mixed $val Value.
protected function _conf_string_val( $id, $val ) {
* If the switch setting is a triple value or not.
* @param string $id Option ID.
protected function _conf_multi_switch( $id ) {
if ( ! empty( self::$_multi_switch_list[ $id ] ) ) {
return self::$_multi_switch_list[ $id ];
if ( self::O_CACHE === $id && is_multisite() ) {
* Append a new multi switch max limit for the bool option.
* @param string $id Option ID.
* @param int $v Max state.
public static function set_multi_switch( $id, $v ) {
self::$_multi_switch_list[ $id ] = $v;
* Generate const name based on $id.
* @param string $id Option ID.
public static function conf_const( $id ) {
return 'LITESPEED_CONF__' . strtoupper( str_replace( '-', '__', $id ) );
* Filter to be used when saving setting.
* @param string $id Option ID.
protected function _conf_filter( $id ) {
self::O_MEDIA_LAZY_EXC => 'uri',
self::O_DEBUG_INC => 'relative',
self::O_DEBUG_EXC => 'relative',
self::O_MEDIA_LAZY_URI_EXC => 'relative',
self::O_CACHE_PRIV_URI => 'relative',
self::O_PURGE_TIMED_URLS => 'relative',
self::O_CACHE_FORCE_URI => 'relative',
self::O_CACHE_FORCE_PUB_URI => 'relative',
self::O_CACHE_EXC => 'relative',
// self::O_OPTM_CSS_EXC => 'uri', // Need to comment out for inline & external CSS
// self::O_OPTM_JS_EXC => 'uri',
self::O_OPTM_EXC => 'relative',
self::O_OPTM_CCSS_SEP_URI => 'uri',
// self::O_OPTM_JS_DEFER_EXC => 'uri',
self::O_OPTM_DNS_PREFETCH => 'domain',
self::O_CDN_ORI => 'noprotocol,trailingslash', // `Original URLs`
// self::O_OPTM_LOCALIZE_DOMAINS => 'noprotocol', // `Localize Resources`
if ( ! empty( $filters[ $id ] ) ) {
* If the setting changes worth a purge or not.
* @param string $id Option ID.
protected function _conf_purge( $id ) {
self::O_MEDIA_LAZY_URI_EXC,
self::O_PURGE_TIMED_URLS,
self::O_CACHE_FORCE_PUB_URI,
return in_array( $id, $check_ids, true );
* If the setting changes worth a purge ALL or not.
* @param string $id Option ID.
protected function _conf_purge_all( $id ) {
$check_ids = [ self::O_CACHE, self::O_ESI, self::O_DEBUG_DISABLE_ALL, self::NETWORK_O_USE_PRIMARY ];
return in_array( $id, $check_ids, true );
* If the setting is a password or not.