372 lines
14 KiB
PHP
372 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* WordPress core function polyfills.
|
|
*
|
|
* Contains verbatim copies of small WordPress core functions, wrapped in
|
|
* function_exists() guards so they are only defined when the running
|
|
* WordPress version does not provide them. This keeps the plugin free of
|
|
* fatals on older WordPress versions.
|
|
*
|
|
* Policy (see docs/):
|
|
* - Plain FUNCTIONS are polyfilled here, copied from WordPress core as they are.
|
|
* - Whole FUNCTIONALITIES (Application Passwords since WP 5.6, Site Health
|
|
* since WP 5.2) are NOT polyfilled; their call sites are availability-gated
|
|
* and the plugin works without them.
|
|
*
|
|
* @package WPVulnerability
|
|
*
|
|
* @since 5.1.3
|
|
*/
|
|
|
|
defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
|
|
|
|
if ( ! function_exists( 'wp_doing_cron' ) ) {
|
|
/**
|
|
* Determines whether the current request is a WordPress cron request.
|
|
*
|
|
* @since 4.8.0
|
|
*
|
|
* @return bool True if it's a WordPress cron request, false otherwise.
|
|
*/
|
|
function wp_doing_cron() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
/**
|
|
* Filters whether the current request is a WordPress cron request.
|
|
*
|
|
* @since 4.8.0
|
|
*
|
|
* @param bool $wp_doing_cron Whether the current request is a WordPress cron request.
|
|
*/
|
|
return apply_filters( 'wp_doing_cron', defined( 'DOING_CRON' ) && DOING_CRON ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Verbatim WordPress core polyfill; core hook names must be preserved.
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'get_main_site_id' ) ) {
|
|
/**
|
|
* Gets the main site ID.
|
|
*
|
|
* @since 4.9.0
|
|
*
|
|
* @param int|null $network_id Optional. The ID of the network for which to get the main site.
|
|
* Defaults to the current network.
|
|
* @return int The ID of the main site.
|
|
*/
|
|
function get_main_site_id( $network_id = null ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
if ( ! is_multisite() ) {
|
|
return get_current_blog_id();
|
|
}
|
|
|
|
$network = get_network( $network_id );
|
|
if ( ! $network ) {
|
|
return 0;
|
|
}
|
|
|
|
return $network->site_id;
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'wp_is_json_request' ) ) {
|
|
/**
|
|
* Checks whether current request is a JSON request, or is expecting a JSON response.
|
|
*
|
|
* @since 5.0.0
|
|
*
|
|
* @return bool True if Accepts or Content-Type headers contain application/json.
|
|
* False otherwise.
|
|
*/
|
|
function wp_is_json_request() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
if ( isset( $_SERVER['HTTP_CONTENT_TYPE'] ) && false !== strpos( $_SERVER['HTTP_CONTENT_TYPE'], 'application/json' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Verbatim WordPress core polyfill; header used only for a strpos comparison.
|
|
return true;
|
|
}
|
|
|
|
if ( isset( $_SERVER['HTTP_ACCEPT'] ) && false !== strpos( $_SERVER['HTTP_ACCEPT'], 'application/json' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Verbatim WordPress core polyfill; header used only for a strpos comparison.
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'sanitize_locale_name' ) ) {
|
|
/**
|
|
* Strips out all characters not allowed in a locale name.
|
|
*
|
|
* @since 6.2.1
|
|
*
|
|
* @param string $locale_name The locale name to be sanitized.
|
|
* @return string The sanitized value.
|
|
*/
|
|
function sanitize_locale_name( $locale_name ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
// Limit to A-Z, a-z, 0-9, '_', '-'.
|
|
$sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $locale_name );
|
|
|
|
/**
|
|
* Filters a sanitized locale name string.
|
|
*
|
|
* @since 6.2.1
|
|
*
|
|
* @param string $sanitized The sanitized locale name.
|
|
* @param string $locale_name The locale name before sanitization.
|
|
*/
|
|
return apply_filters( 'sanitize_locale_name', $sanitized, $locale_name ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Verbatim WordPress core polyfill; core hook names must be preserved.
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'determine_locale' ) ) {
|
|
/**
|
|
* Determines the current locale desired for the request.
|
|
*
|
|
* @since 5.0.0
|
|
*
|
|
* @global string $pagenow The filename of the current screen.
|
|
*
|
|
* @return string The determined locale.
|
|
*/
|
|
function determine_locale() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
/**
|
|
* Filters the locale for the current request prior to the default determination process.
|
|
*
|
|
* Using this filter allows to override the default logic, effectively short-circuiting the function.
|
|
*
|
|
* @since 5.0.0
|
|
*
|
|
* @param string|null $locale The locale to return and short-circuit. Default null.
|
|
*/
|
|
$determined_locale = apply_filters( 'pre_determine_locale', null ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Verbatim WordPress core polyfill; core hook names must be preserved.
|
|
|
|
if ( $determined_locale && is_string( $determined_locale ) ) {
|
|
return $determined_locale;
|
|
}
|
|
|
|
$determined_locale = get_locale();
|
|
|
|
if ( is_admin() ) {
|
|
$determined_locale = get_user_locale();
|
|
}
|
|
|
|
if ( isset( $_GET['_locale'] ) && 'user' === $_GET['_locale'] && wp_is_json_request() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verbatim WordPress core polyfill; the parameter is only compared, never stored or displayed.
|
|
$determined_locale = get_user_locale();
|
|
}
|
|
|
|
if ( ! empty( $_GET['wp_lang'] ) && isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verbatim WordPress core polyfill; the parameter is sanitized below and never stored or displayed.
|
|
$determined_locale = sanitize_locale_name( wp_unslash( $_GET['wp_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verbatim WordPress core polyfill; the parameter is sanitized before use and never stored or displayed.
|
|
}
|
|
|
|
/**
|
|
* Filters the locale for the current request.
|
|
*
|
|
* @since 5.0.0
|
|
*
|
|
* @param string $determined_locale The locale.
|
|
*/
|
|
return apply_filters( 'determine_locale', $determined_locale ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Verbatim WordPress core polyfill; core hook names must be preserved.
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'wp_timezone_string' ) ) {
|
|
/**
|
|
* Retrieves the timezone of the site as a string.
|
|
*
|
|
* @since 5.3.0
|
|
*
|
|
* @return string PHP timezone name or a ±HH:MM offset.
|
|
*/
|
|
function wp_timezone_string() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
$timezone_string = get_option( 'timezone_string' );
|
|
|
|
if ( $timezone_string ) {
|
|
return $timezone_string;
|
|
}
|
|
|
|
$offset = (float) get_option( 'gmt_offset' );
|
|
$hours = (int) $offset;
|
|
$minutes = ( $offset - $hours );
|
|
|
|
$sign = ( $offset < 0 ) ? '-' : '+';
|
|
$abs_hour = abs( $hours );
|
|
$abs_mins = abs( $minutes * 60 );
|
|
$tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
|
|
|
|
return $tz_offset;
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'wp_timezone' ) ) {
|
|
/**
|
|
* Retrieves the timezone of the site as a DateTimeZone object.
|
|
*
|
|
* @since 5.3.0
|
|
*
|
|
* @return DateTimeZone Timezone object.
|
|
*/
|
|
function wp_timezone() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
return new DateTimeZone( wp_timezone_string() );
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'wp_date' ) ) {
|
|
/**
|
|
* Retrieves the date, in localized format.
|
|
*
|
|
* @since 5.3.0
|
|
*
|
|
* @param string $format PHP date format.
|
|
* @param int|null $timestamp Optional. Unix timestamp. Defaults to current time.
|
|
* @param DateTimeZone|null $timezone Optional. Timezone to output result in.
|
|
* Defaults to timezone from site settings.
|
|
* @return string|false The date, translated if locale specifies it.
|
|
* False on invalid timestamp input.
|
|
*/
|
|
function wp_date( $format, $timestamp = null, $timezone = null ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
global $wp_locale;
|
|
|
|
if ( null === $timestamp ) {
|
|
$timestamp = time();
|
|
} elseif ( ! is_numeric( $timestamp ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( ! $timezone ) {
|
|
$timezone = wp_timezone();
|
|
}
|
|
|
|
$datetime = date_create( '@' . $timestamp );
|
|
$datetime->setTimezone( $timezone );
|
|
|
|
if ( empty( $wp_locale->month ) || empty( $wp_locale->weekday ) ) {
|
|
$date = $datetime->format( $format );
|
|
} else {
|
|
// We need to unpack shorthand `r` format because it has parts that might be localized.
|
|
$format = preg_replace( '/(?<!\\\\)r/', DATE_RFC2822, $format );
|
|
|
|
$new_format = '';
|
|
$format_length = strlen( $format );
|
|
$month = $wp_locale->get_month( $datetime->format( 'm' ) );
|
|
$weekday = $wp_locale->get_weekday( $datetime->format( 'w' ) );
|
|
|
|
for ( $i = 0; $i < $format_length; $i++ ) {
|
|
switch ( $format[ $i ] ) {
|
|
case 'D':
|
|
$new_format .= addcslashes( $wp_locale->get_weekday_abbrev( $weekday ), '\\A..Za..z' );
|
|
break;
|
|
case 'F':
|
|
$new_format .= addcslashes( $month, '\\A..Za..z' );
|
|
break;
|
|
case 'l':
|
|
$new_format .= addcslashes( $weekday, '\\A..Za..z' );
|
|
break;
|
|
case 'M':
|
|
$new_format .= addcslashes( $wp_locale->get_month_abbrev( $month ), '\\A..Za..z' );
|
|
break;
|
|
case 'a':
|
|
$new_format .= addcslashes( $wp_locale->get_meridiem( $datetime->format( 'a' ) ), '\\A..Za..z' );
|
|
break;
|
|
case 'A':
|
|
$new_format .= addcslashes( $wp_locale->get_meridiem( $datetime->format( 'A' ) ), '\\A..Za..z' );
|
|
break;
|
|
case '\\':
|
|
$new_format .= $format[ $i ];
|
|
|
|
// If character follows a slash, we add it without translating.
|
|
if ( $i < $format_length ) {
|
|
$new_format .= $format[ ++$i ];
|
|
}
|
|
break;
|
|
default:
|
|
$new_format .= $format[ $i ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
$date = $datetime->format( $new_format );
|
|
|
|
// Core's wp_date() calls wp_maybe_decline_date() unconditionally, but
|
|
// that function only exists since WP 5.4. Guarded here so the polyfill
|
|
// is safe on the declared 4.7 floor (dates are then not declined).
|
|
if ( function_exists( 'wp_maybe_decline_date' ) ) {
|
|
$date = wp_maybe_decline_date( $date );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters the date formatted based on the locale.
|
|
*
|
|
* @since 5.3.0
|
|
*
|
|
* @param string $date Formatted date string.
|
|
* @param string $format Format to display the date.
|
|
* @param int $timestamp Unix timestamp.
|
|
* @param DateTimeZone $timezone Timezone.
|
|
*/
|
|
$date = apply_filters( 'wp_date', $date, $format, $timestamp, $timezone ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Verbatim WordPress core polyfill; core hook names must be preserved.
|
|
|
|
return $date;
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'get_post_datetime' ) ) {
|
|
/**
|
|
* Retrieves post published or modified time as a DateTimeImmutable object instance.
|
|
*
|
|
* The object will be set to the timezone from WordPress settings.
|
|
*
|
|
* @since 5.3.0
|
|
*
|
|
* @param int|WP_Post|null $post Optional. Post ID or post object. Default is global $post object.
|
|
* @param string $field Optional. Published or modified time to use from database.
|
|
* Accepts 'date' or 'modified'. Default 'date'.
|
|
* @param string $source Optional. Local or UTC time to use from database.
|
|
* Accepts 'local' or 'gmt'. Default 'local'.
|
|
* @return DateTimeImmutable|false Time object on success, false on failure.
|
|
*/
|
|
function get_post_datetime( $post = null, $field = 'date', $source = 'local' ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
$post = get_post( $post );
|
|
|
|
if ( ! $post ) {
|
|
return false;
|
|
}
|
|
|
|
$wp_timezone = wp_timezone();
|
|
|
|
if ( 'gmt' === $source ) {
|
|
$time = ( 'modified' === $field ) ? $post->post_modified_gmt : $post->post_date_gmt;
|
|
$timezone = new DateTimeZone( 'UTC' );
|
|
} else {
|
|
$time = ( 'modified' === $field ) ? $post->post_modified : $post->post_date;
|
|
$timezone = $wp_timezone;
|
|
}
|
|
|
|
if ( empty( $time ) || '0000-00-00 00:00:00' === $time ) {
|
|
return false;
|
|
}
|
|
|
|
$datetime = date_create_immutable_from_format( 'Y-m-d H:i:s', $time, $timezone );
|
|
|
|
if ( false === $datetime ) {
|
|
return false;
|
|
}
|
|
|
|
return $datetime->setTimezone( $wp_timezone );
|
|
}
|
|
}
|
|
|
|
if ( ! function_exists( 'get_post_timestamp' ) ) {
|
|
/**
|
|
* Retrieves post published or modified time as a Unix timestamp.
|
|
*
|
|
* @since 5.3.0
|
|
*
|
|
* @param int|WP_Post|null $post Optional. Post ID or post object. Default is global $post object.
|
|
* @param string $field Optional. Published or modified time to use from database.
|
|
* Accepts 'date' or 'modified'. Default 'date'.
|
|
* @return int|false Unix timestamp on success, false on failure.
|
|
*/
|
|
function get_post_timestamp( $post = null, $field = 'date' ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Verbatim WordPress core polyfill.
|
|
$datetime = get_post_datetime( $post, $field );
|
|
|
|
if ( false === $datetime ) {
|
|
return false;
|
|
}
|
|
|
|
return $datetime->getTimestamp();
|
|
}
|
|
}
|