balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-redux-core.php (18330B)


      1 <?php
      2 /**
      3  * Redux Core Class
      4  *
      5  * @class   Redux_Core
      6  * @version 4.0.0
      7  * @package Redux Framework
      8  */
      9 
     10 defined( 'ABSPATH' ) || exit;
     11 
     12 if ( ! class_exists( 'Redux_Core', false ) ) {
     13 
     14 	/**
     15 	 * Class Redux_Core
     16 	 */
     17 	class Redux_Core {
     18 
     19 		/**
     20 		 * Class instance.
     21 		 *
     22 		 * @var object
     23 		 */
     24 		public static $instance;
     25 
     26 		/**
     27 		 * Project version
     28 		 *
     29 		 * @var project string
     30 		 */
     31 		public static $version;
     32 
     33 		/**
     34 		 * Project directory.
     35 		 *
     36 		 * @var project string.
     37 		 */
     38 		public static $dir;
     39 
     40 		/**
     41 		 * Project URL.
     42 		 *
     43 		 * @var project URL.
     44 		 */
     45 		public static $url;
     46 
     47 		/**
     48 		 * Base directory path.
     49 		 *
     50 		 * @var string
     51 		 */
     52 		public static $redux_path;
     53 
     54 		/**
     55 		 * Absolute direction path to WordPress upload directory.
     56 		 *
     57 		 * @var null
     58 		 */
     59 		public static $upload_dir = null;
     60 
     61 		/**
     62 		 * Full URL to WordPress upload directory.
     63 		 *
     64 		 * @var string
     65 		 */
     66 		public static $upload_url = null;
     67 
     68 		/**
     69 		 * Set when Redux is run as a plugin.
     70 		 *
     71 		 * @var bool
     72 		 */
     73 		public static $is_plugin = true;
     74 
     75 		/**
     76 		 * Indicated in_theme or in_plugin.
     77 		 *
     78 		 * @var string
     79 		 */
     80 		public static $installed = '';
     81 
     82 		/**
     83 		 * Set when Redux is run as a plugin.
     84 		 *
     85 		 * @var bool
     86 		 */
     87 		public static $as_plugin = false;
     88 
     89 		/**
     90 		 * Set when Redux is embedded within a theme.
     91 		 *
     92 		 * @var bool
     93 		 */
     94 		public static $in_theme = false;
     95 
     96 		/**
     97 		 * Set when Redux Pro plugin is loaded and active.
     98 		 *
     99 		 * @var bool
    100 		 */
    101 		public static $pro_loaded = false;
    102 
    103 		/**
    104 		 * Pointer to updated google fonts array.
    105 		 *
    106 		 * @var array
    107 		 */
    108 		public static $google_fonts = array();
    109 
    110 		/**
    111 		 * List of files calling Redux.
    112 		 *
    113 		 * @var array
    114 		 */
    115 		public static $callers = array();
    116 
    117 		/**
    118 		 * Nonce.
    119 		 *
    120 		 * @var string
    121 		 */
    122 		public static $wp_nonce;
    123 
    124 		/**
    125 		 * Pointer to _SERVER global.
    126 		 *
    127 		 * @var null
    128 		 */
    129 		public static $server = null;
    130 
    131 		/**
    132 		 * Pointer to the thirdparty fixes class.
    133 		 *
    134 		 * @var null
    135 		 */
    136 		public static $third_party_fixes = null;
    137 
    138 		/**
    139 		 * Redux Welcome screen object.
    140 		 *
    141 		 * @var null
    142 		 */
    143 		public static $welcome = null;
    144 
    145 		/**
    146 		 * Redux Appsero object.
    147 		 *
    148 		 * @var null
    149 		 */
    150 		public static $appsero = null;
    151 
    152 		/**
    153 		 * Redux Insights object.
    154 		 *
    155 		 * @var null
    156 		 */
    157 		public static $insights = null;
    158 
    159 		/**
    160 		 * Flag for Redux Template snables status.
    161 		 *
    162 		 * @var bool
    163 		 */
    164 		public static $redux_templates_enabled = false;
    165 
    166 		/**
    167 		 * Flag for Extendify Template snables status.
    168 		 *
    169 		 * @var bool
    170 		 */
    171 		public static $extendify_templates_enabled = true;
    172 
    173 		/**
    174 		 * Creates instance of class.
    175 		 *
    176 		 * @return Redux_Core
    177 		 * @throws Exception Comment.
    178 		 */
    179 		public static function instance() {
    180 			if ( ! self::$instance ) {
    181 				self::$instance = new self();
    182 
    183 				self::$instance->includes();
    184 				self::$instance->init();
    185 				self::$instance->hooks();
    186 
    187 				add_action( 'plugins_loaded', array( 'Redux_Core', 'plugins_loaded' ) );
    188 			}
    189 
    190 			return self::$instance;
    191 		}
    192 
    193 		/**
    194 		 * Things to run after pluggable.php had loaded.
    195 		 */
    196 		public static function plugins_loaded() {
    197 			Redux_Functions_Ex::pro_to_ext();
    198 		}
    199 
    200 		/**
    201 		 * Class init.
    202 		 */
    203 		private function init() {
    204 
    205 			self::$server = array(
    206 				'SERVER_SOFTWARE' => '',
    207 				'REMOTE_ADDR'     => Redux_Helpers::is_local_host() ? '127.0.0.1' : '',
    208 				'HTTP_USER_AGENT' => '',
    209 				'HTTP_HOST'       => '',
    210 				'REQUEST_URI'     => '',
    211 			);
    212 
    213 			// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
    214 			if ( ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
    215 				self::$server['SERVER_SOFTWARE'] = sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
    216 			}
    217 			if ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
    218 				self::$server['REMOTE_ADDR'] = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
    219 			}
    220 			if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
    221 				self::$server['HTTP_USER_AGENT'] = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
    222 			}
    223 			if ( ! empty( $_SERVER['HTTP_HOST'] ) ) {
    224 				self::$server['HTTP_HOST'] = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) );
    225 			}
    226 			if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
    227 				self::$server['REQUEST_URI'] = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
    228 			}
    229 
    230 			// phpcs:enable
    231 
    232 			self::$dir = trailingslashit( wp_normalize_path( dirname( realpath( __FILE__ ) ) ) );
    233 
    234 			Redux_Functions_Ex::generator();
    235 
    236 			if ( ! class_exists( 'ReduxAppsero\Client' ) ) {
    237 				require_once Redux_Path::get_path( '/appsero/Client.php' );
    238 			}
    239 
    240 			if ( defined( 'REDUX_PLUGIN_FILE' ) ) {
    241 				$client      = new ReduxAppsero\Client( 'f6b61361-757e-4600-bb0f-fe404ae9871b', 'Redux Framework', REDUX_PLUGIN_FILE );
    242 				$plugin_info = Redux_Functions_Ex::is_inside_plugin( REDUX_PLUGIN_FILE );
    243 			} else {
    244 				$client = new ReduxAppsero\Client( 'f6b61361-757e-4600-bb0f-fe404ae9871b', 'Redux Framework', __FILE__ );
    245 				// See if Redux is a plugin or not.
    246 
    247 				$client->slug       = 'redux-framework';
    248 				$client->textdomain = 'redux-framework';
    249 				$client->version    = self::$version;
    250 			}
    251 
    252 			$plugin_info = Redux_Functions_Ex::is_inside_plugin( __FILE__ );
    253 
    254 			if ( false !== $plugin_info ) {
    255 				self::$installed = class_exists( 'Redux_Framework_Plugin' ) ? 'plugin' : 'in_plugin';
    256 				self::$is_plugin = class_exists( 'Redux_Framework_Plugin' );
    257 				self::$as_plugin = true;
    258 				self::$url       = trailingslashit( dirname( $plugin_info['url'] ) );
    259 				if ( isset( $plugin_info['slug'] ) && ! empty( $plugin_info['slug'] ) ) {
    260 					$client->slug = $plugin_info['slug'];
    261 				}
    262 				$client->type = 'plugin';
    263 			} else {
    264 				$theme_info = Redux_Functions_Ex::is_inside_theme( __FILE__ );
    265 				if ( false !== $theme_info ) {
    266 					self::$url       = trailingslashit( dirname( $theme_info['url'] ) );
    267 					self::$in_theme  = true;
    268 					self::$installed = 'in_theme';
    269 					if ( isset( $theme_info['slug'] ) && ! empty( $theme_info['slug'] ) ) {
    270 						$client->slug = $theme_info['slug'];
    271 					}
    272 					$client->type = 'theme';
    273 				}
    274 			}
    275 
    276 			self::$appsero = $client;
    277 
    278 			// Activate insights.
    279 			self::$insights = self::$appsero->insights();
    280 
    281 			if ( class_exists( 'Redux_Pro' ) ) {
    282 				self::$insights->add_extra(
    283 					array(
    284 						'pro'    => Redux_Pro::$version,
    285 						'mokama' => Redux_Helpers::mokama(),
    286 					)
    287 				);
    288 			}
    289 
    290 			self::$insights->hide_notice()->init();
    291 			if ( ! defined( 'REDUX_PLUGIN_FILE' ) ) {
    292 				remove_action( 'admin_footer', array( self::$insights, 'deactivate_scripts' ) );
    293 			}
    294 
    295 			if ( ! self::$insights->tracking_allowed() ) {
    296 				if ( ! self::$insights->notice_dismissed() ) {
    297 					// Old tracking permissions.
    298 					$tracking_options = get_option( 'redux-framework-tracking', array() );
    299 					if ( ! empty( $tracking_options ) ) {
    300 						if ( isset( $tracking_options['allow_tracking'] ) && 'yes' === $tracking_options['allow_tracking'] ) {
    301 							Redux_Functions_Ex::set_activated();
    302 						}
    303 					}
    304 				}
    305 			}
    306 
    307 			remove_action( 'redux_admin_notices_run', array( 'ReduxAppsero\Insights', 'admin_notice' ) );
    308 
    309 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    310 			self::$url = apply_filters( 'redux/url', self::$url );
    311 
    312 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    313 			self::$dir = apply_filters( 'redux/dir', self::$dir );
    314 
    315 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    316 			self::$is_plugin = apply_filters( 'redux/is_plugin', self::$is_plugin );
    317 
    318 			if ( ! function_exists( 'current_time' ) ) {
    319 				require_once ABSPATH . '/wp-includes/functions.php';
    320 			}
    321 
    322 			$upload_dir       = wp_upload_dir();
    323 			self::$upload_dir = $upload_dir['basedir'] . '/redux/';
    324 			self::$upload_url = str_replace( array( 'https://', 'http://' ), '//', $upload_dir['baseurl'] . '/redux/' );
    325 
    326 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    327 			self::$upload_dir = apply_filters( 'redux/upload_dir', self::$upload_dir );
    328 
    329 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    330 			self::$upload_url = apply_filters( 'redux/upload_url', self::$upload_url );
    331 
    332 			self::$redux_templates_enabled     = (bool) get_option( 'use_redux_templates' );
    333 			self::$extendify_templates_enabled = (bool) get_option( 'use_extendify_templates', true );
    334 		}
    335 
    336 		/**
    337 		 * Code to execute on framework __construct.
    338 		 *
    339 		 * @param object $parent Pointer to ReduxFramework object.
    340 		 * @param array  $args   Global arguments array.
    341 		 */
    342 		public static function core_construct( $parent, array $args ) {
    343 			self::$third_party_fixes = new Redux_ThirdParty_Fixes( $parent );
    344 
    345 			Redux_ThemeCheck::get_instance();
    346 
    347 		}
    348 
    349 		/**
    350 		 * Autoregister run.
    351 		 *
    352 		 * @throws Exception Comment.
    353 		 */
    354 		private function includes() {
    355 			if ( class_exists( 'Redux_Pro' ) && isset( Redux_Pro::$dir ) ) {
    356 				self::$pro_loaded = true;
    357 			}
    358 
    359 			require_once dirname( __FILE__ ) . '/inc/classes/class-redux-path.php';
    360 			require_once dirname( __FILE__ ) . '/inc/classes/class-redux-functions-ex.php';
    361 			require_once dirname( __FILE__ ) . '/inc/classes/class-redux-helpers.php';
    362 			require_once dirname( __FILE__ ) . '/inc/classes/class-redux-enable-gutenberg.php';
    363 			require_once dirname( __FILE__ ) . '/inc/classes/class-redux-instances.php';
    364 			Redux_Functions_Ex::register_class_path( 'Redux', dirname( __FILE__ ) . '/inc/classes' );
    365 			Redux_Functions_Ex::register_class_path( 'Redux', dirname( __FILE__ ) . '/inc/welcome' );
    366 			spl_autoload_register( array( $this, 'register_classes' ) );
    367 
    368 			self::$welcome = new Redux_Welcome();
    369 			new Redux_Rest_Api_Builder( $this );
    370 
    371 			add_action( 'admin_init', array( $this, 'admin_init' ) );
    372 
    373 			add_filter( 'debug_information', array( $this, 'add_debug_info' ) );
    374 			add_filter( 'redux/tracking/options', array( 'Redux_Helpers', 'redux_stats_additions' ) );
    375 		}
    376 
    377 		/**
    378 		 * Add debug infor the WP Site Health screen.
    379 		 *
    380 		 * @param array $debug_info Debug data.
    381 		 *
    382 		 * @return array
    383 		 */
    384 		public function add_debug_info( array $debug_info ): array {
    385 
    386 			// Get browser data.
    387 			if ( ! class_exists( 'Browser' ) ) {
    388 				require_once self::$dir . 'inc/lib/browser.php';
    389 			}
    390 
    391 			$browser = new Browser();
    392 
    393 			$browser_data = array(
    394 				'Agent'    => $browser->getUserAgent(),
    395 				'Browser'  => $browser->getBrowser(),
    396 				'Version'  => $browser->getVersion(),
    397 				'Platform' => $browser->getPlatform(),
    398 			);
    399 
    400 			// Set Redux dir permission results to Site Health screen.
    401 			$debug_info['wp-filesystem']['fields'][] = array(
    402 				'label' => esc_html__( 'The Redux upload directory', 'redux-framework' ),
    403 				'value' => wp_is_writable( self::$upload_dir ) ? 'Writable' : 'Not writable',
    404 			);
    405 
    406 			// Set Redux plugin results to Site Health screen.
    407 			$debug_info['redux-framework'] = array(
    408 				'label'       => esc_html__( 'Redux Framework', 'redux-framework' ),
    409 				'description' => esc_html__( 'Debug information specific to Redux Framework.', 'redux-framework' ),
    410 				'fields'      => array(
    411 					'version'        => array(
    412 						'label' => esc_html__( 'Version', 'redux-framework' ),
    413 						'value' => self::$version,
    414 					),
    415 					'installation'   => array(
    416 						'label' => esc_html__( 'Installation', 'redux-framework' ),
    417 						'value' => self::$installed,
    418 					),
    419 					'data directory' => array(
    420 						'label' => esc_html__( 'Data directory', 'redux-framerwork' ),
    421 						'value' => self::$dir,
    422 					),
    423 					'browser'        => array(
    424 						'label' => esc_html__( 'Browser', 'redux-framework' ),
    425 						'value' => $browser_data,
    426 					),
    427 				),
    428 			);
    429 
    430 			$redux = Redux::all_instances();
    431 
    432 			if ( ! empty( $redux ) && is_array( $redux ) ) {
    433 				foreach ( $redux as $inst => $data ) {
    434 					Redux::init( $inst );
    435 
    436 					$inst_name = ucwords( str_replace( array( '_', '-' ), ' ', $inst ) );
    437 					$args      = $data->args;
    438 
    439 					$ext = Redux::get_extensions( $inst );
    440 					if ( ! empty( $ext ) && is_array( $ext ) ) {
    441 						ksort( $ext );
    442 
    443 						foreach ( $ext as $name => $arr ) {
    444 							$ver = $arr['version'];
    445 
    446 							$ex = esc_html( ucwords( str_replace( array( '_', '-' ), ' ', $name ) ) );
    447 
    448 							$extensions[ $ex ] = esc_html( $ver );
    449 						}
    450 					}
    451 
    452 					// Output Redux instances.
    453 					$debug_info[ 'redux-instance-' . $inst ] = array(
    454 						// translators: %s = Instance name.
    455 						'label'       => sprintf( esc_html__( 'Redux Instance: %s', 'redux-framework' ), $inst_name ),
    456 						// translators: %s = Instance name w/ HTML.
    457 						'description' => sprintf( esc_html__( 'Debug information for the %s Redux instance.', 'redux-framework' ), '<code>' . $inst . '</code>' ),
    458 						'fields'      => array(
    459 							'opt_name'         => array(
    460 								'label' => esc_html( 'opt_name' ),
    461 								'value' => $args['opt_name'],
    462 							),
    463 							'global_variable'  => array(
    464 								'label' => esc_html( 'global_variable' ),
    465 								'value' => $args['global_variable'],
    466 							),
    467 							'dev_mode'         => array(
    468 								'label' => esc_html( 'dev_mode' ),
    469 								'value' => $args['dev_mode'] ? 'true' : 'false',
    470 							),
    471 							'ajax_save'        => array(
    472 								'label' => esc_html( 'ajax_save' ),
    473 								'value' => $args['ajax_save'] ? 'true' : 'false',
    474 							),
    475 							'page_slug'        => array(
    476 								'label' => esc_html( 'page_slug' ),
    477 								'value' => $args['page_slug'],
    478 							),
    479 							'page_permissions' => array(
    480 								'label' => esc_html( 'page_permissions' ),
    481 								'value' => $args['page_permissions'],
    482 							),
    483 							'menu_type'        => array(
    484 								'label' => esc_html( 'menu_type' ),
    485 								'value' => $args['menu_type'],
    486 							),
    487 							'page_parent'      => array(
    488 								'label' => esc_html( 'page_parent' ),
    489 								'value' => $args['page_parent'],
    490 							),
    491 							'compiler'         => array(
    492 								'label' => esc_html( 'compiler' ),
    493 								'value' => $args['compiler'] ? 'true' : 'false',
    494 							),
    495 							'output'           => array(
    496 								'label' => esc_html( 'output' ),
    497 								'value' => $args['output'] ? 'true' : 'false',
    498 							),
    499 							'output_tag'       => array(
    500 								'label' => esc_html( 'output_tag' ),
    501 								'value' => $args['output_tag'] ? 'true' : 'false',
    502 							),
    503 							'templates_path'   => array(
    504 								'label' => esc_html( 'templates_path' ),
    505 								'value' => $args['templates_path'],
    506 							),
    507 							'extensions'       => array(
    508 								'label' => esc_html( 'extensions' ),
    509 								'value' => $extensions,
    510 							),
    511 						),
    512 					);
    513 				}
    514 			}
    515 
    516 			return $debug_info;
    517 		}
    518 
    519 		/**
    520 		 * Register callback for autoload.
    521 		 *
    522 		 * @param string $class_name name of class.
    523 		 */
    524 		public function register_classes( string $class_name ) {
    525 			$class_name_test = self::strtolower( $class_name );
    526 
    527 			if ( strpos( $class_name_test, 'redux' ) === false ) {
    528 				return;
    529 			}
    530 
    531 			if ( ! class_exists( 'Redux_Functions_Ex' ) ) {
    532 				require_once Redux_Path::get_path( '/inc/classes/class-redux-functions-ex.php' );
    533 			}
    534 
    535 			if ( ! class_exists( $class_name ) ) {
    536 				// Backward compatibility for extensions sucks!
    537 				if ( 'Redux_Instances' === $class_name ) {
    538 					require_once Redux_Path::get_path( '/inc/classes/class-redux-instances.php' );
    539 					require_once Redux_Path::get_path( '/inc/lib/redux-instances.php' );
    540 
    541 					return;
    542 				}
    543 
    544 				// Load Redux APIs.
    545 				if ( 'Redux' === $class_name ) {
    546 					require_once Redux_Path::get_path( '/inc/classes/class-redux-api.php' );
    547 
    548 					return;
    549 				}
    550 
    551 				// Redux extra theme checks.
    552 				if ( 'Redux_ThemeCheck' === $class_name ) {
    553 					require_once Redux_Path::get_path( '/inc/themecheck/class-redux-themecheck.php' );
    554 
    555 					return;
    556 				}
    557 
    558 				if ( 'Redux_Welcome' === $class_name ) {
    559 					require_once Redux_Path::get_path( '/inc/welcome/class-redux-welcome.php' );
    560 
    561 					return;
    562 				}
    563 
    564 				$mappings = array(
    565 					'ReduxFrameworkInstances'  => 'Redux_Instances',
    566 					'reduxCoreEnqueue'         => '',
    567 					'reduxCorePanel'           => 'Redux_Panel',
    568 					'reduxCoreEnqueue'         => 'Redux_Enqueue',
    569 					'Redux_Abstract_Extension' => 'Redux_Extension_Abstract',
    570 				);
    571 				$alias    = false;
    572 				if ( isset( $mappings[ $class_name ] ) ) {
    573 					$alias      = $class_name;
    574 					$class_name = $mappings[ $class_name ];
    575 				}
    576 
    577 				// Everything else.
    578 				$file = 'class.' . $class_name_test . '.php';
    579 
    580 				$class_path = Redux_Path::get_path( '/inc/classes/' . $file );
    581 
    582 				if ( ! file_exists( $class_path ) ) {
    583 					$class_file_name = str_replace( '_', '-', $class_name );
    584 					$file            = 'class-' . $class_name_test . '.php';
    585 					$class_path      = Redux_Path::get_path( '/inc/classes/' . $file );
    586 				}
    587 
    588 				if ( file_exists( $class_path ) && ! class_exists( $class_name ) ) {
    589 					require_once $class_path;
    590 				}
    591 				if ( class_exists( $class_name ) && ! empty( $alias ) && ! class_exists( $alias ) ) {
    592 					class_alias( $class_name, $alias );
    593 				}
    594 			}
    595 
    596 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    597 			do_action( 'redux/core/includes', $this );
    598 		}
    599 
    600 		/**
    601 		 * Hooks to run on instance creation.
    602 		 */
    603 		private function hooks() {
    604 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    605 			do_action( 'redux/core/hooks', $this );
    606 		}
    607 
    608 		/**
    609 		 * Display the connection banner.
    610 		 */
    611 		public function admin_init() {
    612 			Redux_Connection_Banner::init();
    613 		}
    614 
    615 		/**
    616 		 * Action to run on WordPress heartbeat.
    617 		 *
    618 		 * @return bool
    619 		 */
    620 		public static function is_heartbeat(): bool {
    621 			// Disregard WP AJAX 'heartbeat'call.  Why waste resources?
    622 			if ( isset( $_POST ) && isset( $_POST['_nonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_nonce'] ) ), 'heartbeat-nonce' ) ) {
    623 
    624 				if ( isset( $_POST['action'] ) && 'heartbeat' === sanitize_text_field( wp_unslash( $_POST['action'] ) ) ) {
    625 
    626 					// Hook, for purists.
    627 					if ( has_action( 'redux/ajax/heartbeat' ) ) {
    628 						// phpcs:ignore WordPress.NamingConventions.ValidHookName
    629 						do_action( 'redux/ajax/heartbeat' );
    630 					}
    631 
    632 					return true;
    633 				}
    634 
    635 				return false;
    636 			}
    637 
    638 			// Buh bye!
    639 			return false;
    640 		}
    641 
    642 		/**
    643 		 * Helper method to check for mb_strtolower or to use the standard strtolower.
    644 		 *
    645 		 * @param string $str String to make lowercase.
    646 		 *
    647 		 * @return string|null
    648 		 */
    649 		public static function strtolower( ?string $str ): string {
    650 			if ( function_exists( 'mb_strtolower' ) && function_exists( 'mb_detect_encoding' ) ) {
    651 				return mb_strtolower( $str, mb_detect_encoding( $str ) );
    652 			} else {
    653 				return strtolower( $str );
    654 			}
    655 		}
    656 	}
    657 
    658 	/*
    659 	 * Backwards comparability alias
    660 	 */
    661 	class_alias( 'Redux_Core', 'redux-core' );
    662 }