* These functions are needed to load WordPress.
* Returns the HTTP protocol sent by the server.
* @return string The HTTP protocol. Default: HTTP/1.0.
function wp_get_server_protocol() {
$protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : '';
if ( ! in_array( $protocol, array( 'HTTP/1.1', 'HTTP/2', 'HTTP/2.0', 'HTTP/3' ), true ) ) {
* Fixes `$_SERVER` variables for various setups.
* @global string $PHP_SELF The filename of the currently executing script,
* relative to the document root.
function wp_fix_server_vars() {
$default_server_values = array(
$_SERVER = array_merge( $default_server_values, $_SERVER );
// Fix for IIS when running with PHP ISAPI.
if ( empty( $_SERVER['REQUEST_URI'] )
|| ( 'cgi-fcgi' !== PHP_SAPI && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) )
if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
} elseif ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) {
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
// Use ORIG_PATH_INFO if there is no PATH_INFO.
if ( ! isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) ) {
$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];
// Some IIS + PHP configurations put the script-name in the path-info (no need to append it twice).
if ( isset( $_SERVER['PATH_INFO'] ) ) {
if ( $_SERVER['PATH_INFO'] === $_SERVER['SCRIPT_NAME'] ) {
$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
// Append the query string if it exists and isn't null.
if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
// Fix for PHP as CGI hosts that set SCRIPT_FILENAME to something ending in php.cgi for all requests.
if ( isset( $_SERVER['SCRIPT_FILENAME'] ) && str_ends_with( $_SERVER['SCRIPT_FILENAME'], 'php.cgi' ) ) {
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['PATH_TRANSLATED'];
// Fix for Dreamhost and other PHP as CGI hosts.
if ( isset( $_SERVER['SCRIPT_NAME'] ) && str_contains( $_SERVER['SCRIPT_NAME'], 'php.cgi' ) ) {
unset( $_SERVER['PATH_INFO'] );
$PHP_SELF = $_SERVER['PHP_SELF'];
if ( empty( $PHP_SELF ) ) {
$_SERVER['PHP_SELF'] = preg_replace( '/(\?.*)?$/', '', $_SERVER['REQUEST_URI'] );
$PHP_SELF = $_SERVER['PHP_SELF'];
wp_populate_basic_auth_from_authorization_header();
* Populates the Basic Auth server details from the Authorization header.
* Some servers running in CGI or FastCGI mode don't pass the Authorization
* header on to WordPress. If it's been rewritten to the `HTTP_AUTHORIZATION` header,
* fill in the proper $_SERVER variables instead.
function wp_populate_basic_auth_from_authorization_header() {
// If we don't have anything to pull from, return early.
if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) && ! isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) {
// If either PHP_AUTH key is already set, do nothing.
if ( isset( $_SERVER['PHP_AUTH_USER'] ) || isset( $_SERVER['PHP_AUTH_PW'] ) ) {
// From our prior conditional, one of these must be set.
$header = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
// Test to make sure the pattern matches expected.
if ( ! preg_match( '%^Basic [a-z\d/+]*={0,2}$%i', $header ) ) {
// Removing `Basic ` the token would start six characters in.
$token = substr( $header, 6 );
$userpass = base64_decode( $token );
// There must be at least one colon in the string.
if ( ! str_contains( $userpass, ':' ) ) {
list( $user, $pass ) = explode( ':', $userpass, 2 );
// Now shove them in the proper keys where we're expecting later on.
$_SERVER['PHP_AUTH_USER'] = $user;
$_SERVER['PHP_AUTH_PW'] = $pass;
* Checks the server requirements.
* - MySQL or MariaDB version (unless a database drop-in is present)
* Dies if requirements are not met.
* @global string $required_php_version The minimum required PHP version string.
* @global string[] $required_php_extensions The names of required PHP extensions.
* @global string $wp_version The WordPress version string.
function wp_check_php_mysql_versions() {
global $required_php_version, $required_php_extensions, $wp_version;
$php_version = PHP_VERSION;
if ( version_compare( $required_php_version, $php_version, '>' ) ) {
$protocol = wp_get_server_protocol();
header( sprintf( '%s 500 Internal Server Error', $protocol ), true, 500 );
header( 'Content-Type: text/html; charset=utf-8' );
'Your server is running PHP version %1$s but WordPress %2$s requires at least %3$s.',
$missing_extensions = array();
if ( isset( $required_php_extensions ) && is_array( $required_php_extensions ) ) {
foreach ( $required_php_extensions as $extension ) {
if ( extension_loaded( $extension ) ) {
$missing_extensions[] = sprintf(
'WordPress %1$s requires the <code>%2$s</code> PHP extension.',
if ( count( $missing_extensions ) > 0 ) {
$protocol = wp_get_server_protocol();
header( sprintf( '%s 500 Internal Server Error', $protocol ), true, 500 );
header( 'Content-Type: text/html; charset=utf-8' );
echo implode( '<br>', $missing_extensions );
// This runs before default constants are defined, so we can't assume WP_CONTENT_DIR is set yet.
$wp_content_dir = defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : ABSPATH . 'wp-content';
if ( ! function_exists( 'mysqli_connect' )
&& ! file_exists( $wp_content_dir . '/db.php' )
require_once ABSPATH . WPINC . '/functions.php';
wp_load_translations_early();
$message = '<p>' . __( 'Your PHP installation appears to be missing the MySQL extension which is required by WordPress.' ) . "</p>\n";
$message .= '<p>' . sprintf(
/* translators: %s: mysqli. */
__( 'Please check that the %s PHP extension is installed and enabled.' ),
$message .= '<p>' . sprintf(
/* translators: %s: Support forums URL. */
__( 'If you are unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href="%s">WordPress support forums</a>.' ),
__( 'https://wordpress.org/support/forums/' )
'code' => 'mysql_not_found',
__( 'Requirements Not Met' ),
* Retrieves the current environment type.
* The type can be set via the `WP_ENVIRONMENT_TYPE` global system variable,
* or a constant of the same name.
* Possible values are 'local', 'development', 'staging', and 'production'.
* If not set, the type defaults to 'production'.
* @since 5.5.1 Added the 'local' type.
* @since 5.5.1 Removed the ability to alter the list of types.
* @return string The current environment type.
function wp_get_environment_type() {
static $current_env = '';
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && $current_env ) {
$wp_environments = array(
// Add a note about the deprecated WP_ENVIRONMENT_TYPES constant.
if ( defined( 'WP_ENVIRONMENT_TYPES' ) && function_exists( '_deprecated_argument' ) ) {
if ( function_exists( '__' ) ) {
/* translators: %s: WP_ENVIRONMENT_TYPES */
$message = sprintf( __( 'The %s constant is no longer supported.' ), 'WP_ENVIRONMENT_TYPES' );
$message = sprintf( 'The %s constant is no longer supported.', 'WP_ENVIRONMENT_TYPES' );
// Check if the environment variable has been set, if `getenv` is available on the system.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( false !== $has_env ) {
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
$current_env = WP_ENVIRONMENT_TYPE;
// Make sure the environment is an allowed one, and not accidentally set to an invalid value.
if ( ! in_array( $current_env, $wp_environments, true ) ) {
$current_env = 'production';
* Retrieves the current development mode.
* The development mode affects how certain parts of the WordPress application behave,
* which is relevant when developing for WordPress.
* Development mode can be set via the `WP_DEVELOPMENT_MODE` constant in `wp-config.php`.
* Possible values are 'core', 'plugin', 'theme', 'all', or an empty string to disable
* development mode. 'all' is a special value to signify that all three development modes
* ('core', 'plugin', and 'theme') are enabled.
* Development mode is considered separately from `WP_DEBUG` and wp_get_environment_type().
* It does not affect debugging output, but rather functional nuances in WordPress.
* This function retrieves the currently set development mode value. To check whether
* a specific development mode is enabled, use wp_is_development_mode().
* @return string The current development mode.
function wp_get_development_mode() {
static $current_mode = null;
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && null !== $current_mode ) {
$development_mode = WP_DEVELOPMENT_MODE;
// Exclusively for core tests, rely on the `$_wp_tests_development_mode` global.
if ( defined( 'WP_RUN_CORE_TESTS' ) && isset( $GLOBALS['_wp_tests_development_mode'] ) ) {
$development_mode = $GLOBALS['_wp_tests_development_mode'];
if ( ! in_array( $development_mode, $valid_modes, true ) ) {
$current_mode = $development_mode;
* Checks whether the site is in the given development mode.
* @param string $mode Development mode to check for. Either 'core', 'plugin', 'theme', or 'all'.
* @return bool True if the given mode is covered by the current development mode, false otherwise.
function wp_is_development_mode( $mode ) {
$current_mode = wp_get_development_mode();
if ( empty( $current_mode ) ) {
// Return true if the current mode encompasses all modes.
if ( 'all' === $current_mode ) {
// Return true if the current mode is the given mode.
return $mode === $current_mode;
* Ensures all of WordPress is not loaded when handling a favicon.ico request.
* Instead, send the headers for a zero-length favicon and bail.
* @deprecated 5.4.0 Deprecated in favor of do_favicon().
function wp_favicon_request() {
if ( '/favicon.ico' === $_SERVER['REQUEST_URI'] ) {
header( 'Content-Type: image/vnd.microsoft.icon' );
* Dies with a maintenance message when conditions are met.
* The default message can be replaced by using a drop-in (maintenance.php in
* the wp-content directory).
function wp_maintenance() {
// Return if maintenance mode is disabled.
if ( ! wp_is_maintenance_mode() ) {
if ( file_exists( WP_CONTENT_DIR . '/maintenance.php' ) ) {
require_once WP_CONTENT_DIR . '/maintenance.php';
require_once ABSPATH . WPINC . '/functions.php';
wp_load_translations_early();
header( 'Retry-After: 600' );
__( 'Briefly unavailable for scheduled maintenance. Check back in a minute.' ),
* Checks if maintenance mode is enabled.
* Checks for a file in the WordPress root directory named ".maintenance".
* This file will contain the variable $upgrading, set to the time the file
* was created. If the file was created less than 10 minutes ago, WordPress
* is in maintenance mode.
* @global int $upgrading The Unix timestamp marking when upgrading WordPress began.
* @return bool True if maintenance mode is enabled, false otherwise.
function wp_is_maintenance_mode() {
if ( ! file_exists( ABSPATH . '.maintenance' ) || wp_installing() ) {
require ABSPATH . '.maintenance';
// If the $upgrading timestamp is older than 10 minutes, consider maintenance over.
if ( ( time() - $upgrading ) >= 10 * MINUTE_IN_SECONDS ) {
// Don't enable maintenance mode while scraping for fatal errors.
if ( is_int( $upgrading ) && isset( $_REQUEST['wp_scrape_key'], $_REQUEST['wp_scrape_nonce'] ) ) {
$key = stripslashes( $_REQUEST['wp_scrape_key'] );
$nonce = stripslashes( $_REQUEST['wp_scrape_nonce'] );
if ( md5( $upgrading ) === $key && (int) $nonce === $upgrading ) {
* Filters whether to enable maintenance mode.
* This filter runs before it can be used by plugins. It is designed for
* non-web runtimes. If this filter returns true, maintenance mode will be
* active and the request will end. If false, the request will be allowed to
* continue processing even if maintenance mode should be active.
* @param bool $enable_checks Whether to enable maintenance mode. Default true.
* @param int $upgrading The timestamp set in the .maintenance file.
if ( ! apply_filters( 'enable_maintenance_mode', true, $upgrading ) ) {
* Gets the time elapsed so far during this PHP script.
* @return float Seconds since the PHP script started.
return microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'];
* Starts the WordPress micro-timer.
* @global float $timestart Unix timestamp set at the beginning of the page load.