ectly encoded entity. */ function wp_kses_normalize_entities2( $matches ) { if ( empty( $matches[1] ) ) { return ''; } $i = $matches[1]; if ( valid_unicode( $i ) ) { $i = str_pad( ltrim( $i, '0' ), 3, '0', STR_PAD_LEFT ); $i = "&#$i;"; } else { $i = "&#$i;"; } return $i; } /** * Callback for `wp_kses_normalize_entities()` for regular expression. * * This function helps `wp_kses_normalize_entities()` to only accept valid Unicode * numeric entities in hex form. * * @since 2.7.0 * @access private * @ignore * * @param array $matches `preg_replace_callback()` matches array. * @return string Correctly encoded entity. */ function wp_kses_normalize_entities3( $matches ) { if ( empty( $matches[1] ) ) { return ''; } $hexchars = $matches[1]; return ( ! valid_unicode( hexdec( $hexchars ) ) ) ? "&#x$hexchars;" : '&#x' . ltrim( $hexchars, '0' ) . ';'; } /** * Determines if a Unicode codepoint is valid. * * @since 2.7.0 * * @param int $i Unicode codepoint. * @return bool Whether or not the codepoint is a valid Unicode codepoint. */ function valid_unicode( $i ) { return ( 0x9 == $i || 0xa == $i || 0xd == $i || ( 0x20 <= $i && $i <= 0xd7ff ) || ( 0xe000 <= $i && $i <= 0xfffd ) || ( 0x10000 <= $i && $i <= 0x10ffff ) ); } /** * Converts all numeric HTML entities to their named counterparts. * * This function decodes numeric HTML entities (`A` and `A`). * It doesn't do anything with named entities like `ä`, but we don't * need them in the allowed URL protocols system anyway. * * @since 1.0.0 * * @param string $string Content to change entities. * @return string Content after decoded entities. */ function wp_kses_decode_entities( $string ) { $string = preg_replace_callback( '/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string ); $string = preg_replace_callback( '/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string ); return $string; } /** * Regex callback for `wp_kses_decode_entities()`. * * @since 2.9.0 * @access private * @ignore * * @param array $match preg match * @return string */ function _wp_kses_decode_entities_chr( $match ) { return chr( $match[1] ); } /** * Regex callback for `wp_kses_decode_entities()`. * * @since 2.9.0 * @access private * @ignore * * @param array $match preg match * @return string */ function _wp_kses_decode_entities_chr_hexdec( $match ) { return chr( hexdec( $match[1] ) ); } /** * Sanitize content with allowed HTML KSES rules. * * This function expects slashed data. * * @since 1.0.0 * * @param string $data Content to filter, expected to be escaped with slashes. * @return string Filtered content. */ function wp_filter_kses( $data ) { return addslashes( wp_kses( stripslashes( $data ), current_filter() ) ); } /** * Sanitize content with allowed HTML KSES rules. * * This function expects unslashed data. * * @since 2.9.0 * * @param string $data Content to filter, expected to not be escaped. * @return string Filtered content. */ function wp_kses_data( $data ) { return wp_kses( $data, current_filter() ); } /** * Sanitizes content for allowed HTML tags for post content. * * Post content refers to the page contents of the 'post' type and not `$_POST` * data from forms. * * This function expects slashed data. * * @since 2.0.0 * * @param string $data Post content to filter, expected to be escaped with slashes. * @return string Filtered post content with allowed HTML tags and attributes intact. */ function wp_filter_post_kses( $data ) { return addslashes( wp_kses( stripslashes( $data ), 'post' ) ); } /** * Sanitizes content for allowed HTML tags for post content. * * Post content refers to the page contents of the 'post' type and not `$_POST` * data from forms. * * This function expects unslashed data. * * @since 2.9.0 * * @param string $data Post content to filter. * @return string Filtered post content with allowed HTML tags and attributes intact. */ function wp_kses_post( $data ) { return wp_kses( $data, 'post' ); } /** * Navigates through an array, object, or scalar, and sanitizes content for * allowed HTML tags for post content. * * @since 4.4.2 * * @see map_deep() * * @param mixed $data The array, object, or scalar value to inspect. * @return mixed The filtered content. */ function wp_kses_post_deep( $data ) { return map_deep( $data, 'wp_kses_post' ); } /** * Strips all HTML from a text string. * * This function expects slashed data. * * @since 2.1.0 * * @param string $data Content to strip all HTML from. * @return string Filtered content without any HTML. */ function wp_filter_nohtml_kses( $data ) { return addslashes( wp_kses( stripslashes( $data ), 'strip' ) ); } /** * Adds all KSES input form content filters. * * All hooks have default priority. The `wp_filter_kses()` function is added to * the 'pre_comment_content' and 'title_save_pre' hooks. * * The `wp_filter_post_kses()` function is added to the 'content_save_pre', * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks. * * @since 2.0.0 */ function kses_init_filters() { // Normal filtering. add_filter( 'title_save_pre', 'wp_filter_kses' ); // Comment filtering. if ( current_user_can( 'unfiltered_html' ) ) { add_filter( 'pre_comment_content', 'wp_filter_post_kses' ); } else { add_filter( 'pre_comment_content', 'wp_filter_kses' ); } // Post filtering. add_filter( 'content_save_pre', 'wp_filter_post_kses' ); add_filter( 'excerpt_save_pre', 'wp_filter_post_kses' ); add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); } /** * Removes all KSES input form content filters. * * A quick procedural method to removing all of the filters that KSES uses for * content in WordPress Loop. * * Does not remove the `kses_init()` function from {@see 'init'} hook (priority is * default). Also does not remove `kses_init()` function from {@see 'set_current_user'} * hook (priority is also default). * * @since 2.0.6 */ function kses_remove_filters() { // Normal filtering. remove_filter( 'title_save_pre', 'wp_filter_kses' ); // Comment filtering. remove_filter( 'pre_comment_content', 'wp_filter_post_kses' ); remove_filter( 'pre_comment_content', 'wp_filter_kses' ); // Post filtering. remove_filter( 'content_save_pre', 'wp_filter_post_kses' ); remove_filter( 'excerpt_save_pre', 'wp_filter_post_kses' ); remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' ); } /** * Sets up most of the KSES filters for input form content. * * First removes all of the KSES filters in case the current user does not need * to have KSES filter the content. If the user does not have `unfiltered_html` * capability, then KSES filters are added. * * @since 2.0.0 */ function kses_init() { kses_remove_filters(); if ( ! current_user_can( 'unfiltered_html' ) ) { kses_init_filters(); } } /** * Filters an inline style attribute and removes disallowed rules. * * @since 2.8.1 * * @param string $css A string of CSS rules. * @param string $deprecated Not used. * @return string Filtered string of CSS rules. */ function safecss_filter_attr( $css, $deprecated = '' ) { if ( ! empty( $deprecated ) ) { _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented. } $css = wp_kses_no_null( $css ); $css = str_replace( array( "\n", "\r", "\t" ), '', $css ); $allowed_protocols = wp_allowed_protocols(); $css_array = explode( ';', trim( $css ) ); /** * Filters list of allowed CSS attributes. * * @since 2.8.1 * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`. * @since 4.6.0 Added support for `list-style-type`. * @since 5.0.0 Added support for `background-image`. * @since 5.1.0 Added support for `text-transform`. * @since 5.2.0 Added support for `background-position` and `grid-template-columns`. * @since 5.3.0 Added support for `grid`, `flex` and `column` layout properties. * Extend `background-*` support of individual properties. * @since 5.3.1 Added support for gradient backgrounds. * @since 5.7.1 Added support for `object-position`. * @since 5.8.0 Added support for `calc()` and `var()` values. * * @param string[] $attr Array of allowed CSS attributes. */ $allowed_attr = apply_filters( 'safe_style_css', array( 'background', 'background-color', 'background-image', 'background-position', 'background-size', 'background-attachment', 'background-blend-mode', 'border', 'border-radius', 'border-width', 'border-color', 'border-style', 'border-right', 'border-right-color', 'border-right-style', 'border-right-width', 'border-bottom', 'border-bottom-color', 'border-bottom-style', 'border-bottom-width', 'border-left', 'border-left-color', 'border-left-style', 'border-left-width', 'border-top', 'border-top-color', 'border-top-style', 'border-top-width', 'border-spacing', 'border-collapse', 'caption-side', 'columns', 'column-count', 'column-fill', 'column-gap', 'column-rule', 'column-span', 'column-width', 'color', 'font', 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight', 'letter-spacing', 'line-height', 'text-align', 'text-decoration', 'text-indent', 'text-transform', 'height', 'min-height', 'max-height', 'width', 'min-width', 'max-width', 'margin', 'margin-right', 'margin-bottom', 'margin-left', 'margin-top', 'padding', 'padding-right', 'padding-bottom', 'padding-left', 'padding-top', 'flex', 'flex-basis', 'flex-direction', 'flex-flow', 'flex-grow', 'flex-shrink', 'grid-template-columns', 'grid-auto-columns', 'grid-column-start', 'grid-column-end', 'grid-column-gap', 'grid-template-rows', 'grid-auto-rows', 'grid-row-start', 'grid-row-end', 'grid-row-gap', 'grid-gap', 'justify-content', 'justify-items', 'justify-self', 'align-content', 'align-items', 'align-self', 'clear', 'cursor', 'direction', 'float', 'list-style-type', 'object-position', 'overflow', 'vertical-align', ) ); /* * CSS attributes that accept URL data types. * * This is in accordance to the CSS spec and unrelated to * the sub-set of supported attributes above. * * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url */ $css_url_data_types = array( 'background', 'background-image', 'cursor', 'list-style', 'list-style-image', ); /* * CSS attributes that accept gradient data types. * */ $css_gradient_data_types = array( 'background', 'background-image', ); if ( empty( $allowed_attr ) ) { return $css; } $css = ''; foreach ( $css_array as $css_item ) { if ( '' === $css_item ) { continue; } $css_item = trim( $css_item ); $css_test_string = $css_item; $found = false; $url_attr = false; $gradient_attr = false; if ( strpos( $css_item, ':' ) === false ) { $found = true; } else { $parts = explode( ':', $css_item, 2 ); $css_selector = trim( $parts[0] ); if ( in_array( $css_selector, $allowed_attr, true ) ) { $found = true; $url_attr = in_array( $css_selector, $css_url_data_types, true ); $gradient_attr = in_array( $css_selector, $css_gradient_data_types, true ); } } if ( $found && $url_attr ) { // Simplified: matches the sequence `url(*)`. preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches ); foreach ( $url_matches[0] as $url_match ) { // Clean up the URL from each of the matches above. preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces ); if ( empty( $url_pieces[2] ) ) { $found = false; break; } $url = trim( $url_pieces[2] ); if ( empty( $url ) || wp_kses_bad_protocol( $url, $allowed_protocols ) !== $url ) { $found = false; break; } else { // Remove the whole `url(*)` bit that was matched above from the CSS. $css_test_string = str_replace( $url_match, '', $css_test_string ); } } } if ( $found && $gradient_attr ) { $css_value = trim( $parts[1] ); if ( preg_match( '/^(repeating-)?(linear|radial|conic)-gradient\(([^()]|rgb[a]?\([^()]*\))*\)$/', $css_value ) ) { // Remove the whole `gradient` bit that was matched above from the CSS. $css_test_string = str_replace( $css_value, '', $css_test_string ); } } if ( $found ) { // Allow CSS calc(). $css_test_string = preg_replace( '/calc\(((?:\([^()]*\)?|[^()])*)\)/', '', $css_test_string ); // Allow CSS var(). $css_test_string = preg_replace( '/\(?var\(--[a-zA-Z0-9_-]*\)/', '', $css_test_string ); // Check for any CSS containing \ ( & } = or comments, // except for url(), calc(), or var() usage checked above. $allow_css = ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ); /** * Filters the check for unsafe CSS in `safecss_filter_attr`. * * Enables developers to determine whether a section of CSS should be allowed or discarded. * By default, the value will be false if the part contains \ ( & } = or comments. * Return true to allow the CSS part to be included in the output. * * @since 5.5.0 * * @param bool $allow_css Whether the CSS in the test string is considered safe. * @param string $css_test_string The CSS string to test. */ $allow_css = apply_filters( 'safecss_filter_attr_allow_css', $allow_css, $css_test_string ); // Only add the CSS part if it passes the regex check. if ( $allow_css ) { if ( '' !== $css ) { $css .= ';'; } $css .= $css_item; } } } return $css; } /** * Helper function to add global attributes to a tag in the allowed HTML list. * * @since 3.5.0 * @since 5.0.0 Add support for `data-*` wildcard attributes. * @access private * @ignore * * @param array $value An array of attributes. * @return array The array of attributes with global attributes added. */ function _wp_add_global_attributes( $value ) { $global_attributes = array( 'aria-describedby' => true, 'aria-details' => true, 'aria-label' => true, 'aria-labelledby' => true, 'aria-hidden' => true, 'class' => true, 'id' => true, 'style' => true, 'title' => true, 'role' => true, 'data-*' => true, ); if ( true === $value ) { $value = array(); } if ( is_array( $value ) ) { return array_merge( $value, $global_attributes ); } return $value; } hook ] ) ) { $results[] = count( $crons[ $timestamp ][ $hook ] ); } unset( $crons[ $timestamp ][ $hook ] ); if ( empty( $crons[ $timestamp ] ) ) { unset( $crons[ $timestamp ] ); } } /* * If the results are empty (zero events to unschedule), no attempt * to update the cron array is required. */ if ( empty( $results ) ) { return 0; } $set = _set_cron_array( $crons, $wp_error ); if ( true === $set ) { return array_sum( $results ); } return $set; } /** * Retrieve a scheduled event. * * Retrieve the full event object for a given event, if no timestamp is specified the next * scheduled event is returned. * * @since 5.1.0 * * @param string $hook Action hook of the event. * @param array $args Optional. Array containing each separate argument to pass to the hook's callback function. * Although not passed to a callback, these arguments are used to uniquely identify the * event, so they should be the same as those used when originally scheduling the event. * Default empty array. * @param int|null $timestamp Optional. Unix timestamp (UTC) of the event. If not specified, the next scheduled event * is returned. Default null. * @return object|false The event object. False if the event does not exist. */ function wp_get_scheduled_event( $hook, $args = array(), $timestamp = null ) { /** * Filter to preflight or hijack retrieving a scheduled event. * * Returning a non-null value will short-circuit the normal process, * returning the filtered value instead. * * Return false if the event does not exist, otherwise an event object * should be returned. * * @since 5.1.0 * * @param null|false|object $pre Value to return instead. Default null to continue retrieving the event. * @param string $hook Action hook of the event. * @param array $args Array containing each separate argument to pass to the hook's callback function. * Although not passed to a callback, these arguments are used to uniquely identify * the event. * @param int|null $timestamp Unix timestamp (UTC) of the event. Null to retrieve next scheduled event. */ $pre = apply_filters( 'pre_get_scheduled_event', null, $hook, $args, $timestamp ); if ( null !== $pre ) { return $pre; } if ( null !== $timestamp && ! is_numeric( $timestamp ) ) { return false; } $crons = _get_cron_array(); if ( empty( $crons ) ) { return false; } $key = md5( serialize( $args ) ); if ( ! $timestamp ) { // Get next event. $next = false; foreach ( $crons as $timestamp => $cron ) { if ( isset( $cron[ $hook ][ $key ] ) ) { $next = $timestamp; break; } } if ( ! $next ) { return false; } $timestamp = $next; } elseif ( ! isset( $crons[ $timestamp ][ $hook ][ $key ] ) ) { return false; } $event = (object) array( 'hook' => $hook, 'timestamp' => $timestamp, 'schedule' => $crons[ $timestamp ][ $hook ][ $key ]['schedule'], 'args' => $args, ); if ( isset( $crons[ $timestamp ][ $hook ][ $key ]['interval'] ) ) { $event->interval = $crons[ $timestamp ][ $hook ][ $key ]['interval']; } return $event; } /** * Retrieve the next timestamp for an event. * * @since 2.1.0 * * @param string $hook Action hook of the event. * @param array $args Optional. Array containing each separate argument to pass to the hook's callback function. * Although not passed to a callback, these arguments are used to uniquely identify the * event, so they should be the same as those used when originally scheduling the event. * Default empty array. * @return int|false The Unix timestamp of the next time the event will occur. False if the event doesn't exist. */ function wp_next_scheduled( $hook, $args = array() ) { $next_event = wp_get_scheduled_event( $hook, $args ); if ( ! $next_event ) { return false; } return $next_event->timestamp; } /** * Sends a request to run cron through HTTP request that doesn't halt page loading. * * @since 2.1.0 * @since 5.1.0 Return values added. * * @param int $gmt_time Optional. Unix timestamp (UTC). Default 0 (current time is used). * @return bool True if spawned, false if no events spawned. */ function spawn_cron( $gmt_time = 0 ) { if ( ! $gmt_time ) { $gmt_time = microtime( true ); } if ( defined( 'DOING_CRON' ) || isset( $_GET['doing_wp_cron'] ) ) { return false; } /* * Get the cron lock, which is a Unix timestamp of when the last cron was spawned * and has not finished running. * * Multiple processes on multiple web servers can run this code concurrently, * this lock attempts to make spawning as atomic as possible. */ $lock = get_transient( 'doing_cron' ); if ( $lock > $gmt_time + 10 * MINUTE_IN_SECONDS ) { $lock = 0; } // Don't run if another process is currently running it or more than once every 60 sec. if ( $lock + WP_CRON_LOCK_TIMEOUT > $gmt_time ) { return false; } // Sanity check. $crons = wp_get_ready_cron_jobs(); if ( empty( $crons ) ) { return false; } $keys = array_keys( $crons ); if ( isset( $keys[0] ) && $keys[0] > $gmt_time ) { return false; } if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) { if ( 'GET' !== $_SERVER['REQUEST_METHOD'] || defined( 'DOING_AJAX' ) || defined( 'XMLRPC_REQUEST' ) ) { return false; } $doing_wp_cron = sprintf( '%.22F', $gmt_time ); set_transient( 'doing_cron', $doing_wp_cron ); ob_start(); wp_redirect( add_query_arg( 'doing_wp_cron', $doing_wp_cron, wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); echo ' '; // Flush any buffers and send the headers. wp_ob_end_flush_all(); flush(); include_once ABSPATH . 'wp-cron.php'; return true; } // Set the cron lock with the current unix timestamp, when the cron is being spawned. $doing_wp_cron = sprintf( '%.22F', $gmt_time ); set_transient( 'doing_cron', $doing_wp_cron ); /** * Filters the cron request arguments. * * @since 3.5.0 * @since 4.5.0 The `$doing_wp_cron` parameter was added. * * @param array $cron_request_array { * An array of cron request URL arguments. * * @type string $url The cron request URL. * @type int $key The 22 digit GMT microtime. * @type array $args { * An array of cron request arguments. * * @type int $timeout The request timeout in seconds. Default .01 seconds. * @type bool $blocking Whether to set blocking for the request. Default false. * @type bool $sslverify Whether SSL should be verified for the request. Default false. * } * } * @param string $doing_wp_cron The unix timestamp of the cron lock. */ $cron_request = apply_filters( 'cron_request', array( 'url' => add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) ), 'key' => $doing_wp_cron, 'args' => array( 'timeout' => 0.01, 'blocking' => false, /** This filter is documented in wp-includes/class-wp-http-streams.php */ 'sslverify' => apply_filters( 'https_local_ssl_verify', false ), ), ), $doing_wp_cron ); $result = wp_remote_post( $cron_request['url'], $cron_request['args'] ); return ! is_wp_error( $result ); } /** * Register _wp_cron() to run on the {@see 'wp_loaded'} action. * * If the {@see 'wp_loaded'} action has already fired, this function calls * _wp_cron() directly. * * Warning: This function may return Boolean FALSE, but may also return a non-Boolean * value which evaluates to FALSE. For information about casting to booleans see the * {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use * the `===` operator for testing the return value of this function. * * @since 2.1.0 * @since 5.1.0 Return value added to indicate success or failure. * @since 5.7.0 Functionality moved to _wp_cron() to which this becomes a wrapper. * * @return bool|int|void On success an integer indicating number of events spawned (0 indicates no * events needed to be spawned), false if spawning fails for one or more events or * void if the function registered _wp_cron() to run on the action. */ function wp_cron() { if ( did_action( 'wp_loaded' ) ) { return _wp_cron(); } add_action( 'wp_loaded', '_wp_cron', 20 ); } /** * Run scheduled callbacks or spawn cron for all scheduled events. * * Warning: This function may return Boolean FALSE, but may also return a non-Boolean * value which evaluates to FALSE. For information about casting to booleans see the * {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use * the `===` operator for testing the return value of this function. * * @since 5.7.0 * @access private * * @return int|false On success an integer indicating number of events spawned (0 indicates no * events needed to be spawned), false if spawning fails for one or more events. */ function _wp_cron() { // Prevent infinite loops caused by lack of wp-cron.php. if ( strpos( $_SERVER['REQUEST_URI'], '/wp-cron.php' ) !== false || ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ) { return 0; } $crons = wp_get_ready_cron_jobs(); if ( empty( $crons ) ) { return 0; } $gmt_time = microtime( true ); $keys = array_keys( $crons ); if ( isset( $keys[0] ) && $keys[0] > $gmt_time ) { return 0; } $schedules = wp_get_schedules(); $results = array(); foreach ( $crons as $timestamp => $cronhooks ) { if ( $timestamp > $gmt_time ) { break; } foreach ( (array) $cronhooks as $hook => $args ) { if ( isset( $schedules[ $hook ]['callback'] ) && ! call_user_func( $schedules[ $hook ]['callback'] ) ) { continue; } $results[] = spawn_cron( $gmt_time ); break 2; } } if ( in_array( false, $results, true ) ) { return false; } return count( $results ); } /** * Retrieve supported event recurrence schedules. * * The default supported recurrences are 'hourly', 'twicedaily', 'daily', and 'weekly'. * A plugin may add more by hooking into the {@see 'cron_schedules'} filter. * The filter accepts an array of arrays. The outer array has a key that is the name * of the schedule, for example 'monthly'. The value is an array with two keys, * one is 'interval' and the other is 'display'. * * The 'interval' is a number in seconds of when the cron job should run. * So for 'hourly' the time is `HOUR_IN_SECONDS` (60 * 60 or 3600). For 'monthly', * the value would be `MONTH_IN_SECONDS` (30 * 24 * 60 * 60 or 2592000). * * The 'display' is the description. For the 'monthly' key, the 'display' * would be `__( 'Once Monthly' )`. * * For your plugin, you will be passed an array. You can easily add your * schedule by doing the following. * * // Filter parameter variable name is 'array'. * $array['monthly'] = array( * 'interval' => MONTH_IN_SECONDS, * 'display' => __( 'Once Monthly' ) * ); * * @since 2.1.0 * @since 5.4.0 The 'weekly' schedule was added. * * @return array */ function wp_get_schedules() { $schedules = array( 'hourly' => array( 'interval' => HOUR_IN_SECONDS, 'display' => __( 'Once Hourly' ), ), 'twicedaily' => array( 'interval' => 12 * HOUR_IN_SECONDS, 'display' => __( 'Twice Daily' ), ), 'daily' => array( 'interval' => DAY_IN_SECONDS, 'display' => __( 'Once Daily' ), ), 'weekly' => array( 'interval' => WEEK_IN_SECONDS, 'display' => __( 'Once Weekly' ), ), ); /** * Filters the non-default cron schedules. * * @since 2.1.0 * * @param array $new_schedules An array of non-default cron schedules. Default empty. */ return array_merge( apply_filters( 'cron_schedules', array() ), $schedules ); } /** * Retrieve the recurrence schedule for an event. * * @see wp_get_schedules() for available schedules. * * @since 2.1.0 * @since 5.1.0 {@see 'get_schedule'} filter added. * * @param string $hook Action hook to identify the event. * @param array $args Optional. Arguments passed to the event's callback function. * Default empty array. * @return string|false Schedule name on success, false if no schedule. */ function wp_get_schedule( $hook, $args = array() ) { $schedule = false; $event = wp_get_scheduled_event( $hook, $args ); if ( $event ) { $schedule = $event->schedule; } /** * Filters the schedule for a hook. * * @since 5.1.0 * * @param string|false $schedule Schedule for the hook. False if not found. * @param string $hook Action hook to execute when cron is run. * @param array $args Arguments to pass to the hook's callback function. */ return apply_filters( 'get_schedule', $schedule, $hook, $args ); } /** * Retrieve cron jobs ready to be run. * * Returns the results of _get_cron_array() limited to events ready to be run, * ie, with a timestamp in the past. * * @since 5.1.0 * * @return array Cron jobs ready to be run. */ function wp_get_ready_cron_jobs() { /** * Filter to preflight or hijack retrieving ready cron jobs. * * Returning an array will short-circuit the normal retrieval of ready * cron jobs, causing the function to return the filtered value instead. * * @since 5.1.0 * * @param null|array $pre Array of ready cron tasks to return instead. Default null * to continue using results from _get_cron_array(). */ $pre = apply_filters( 'pre_get_ready_cron_jobs', null ); if ( null !== $pre ) { return $pre; } $crons = _get_cron_array(); if ( false === $crons ) { return array(); } $gmt_time = microtime( true ); $keys = array_keys( $crons ); if ( isset( $keys[0] ) && $keys[0] > $gmt_time ) { return array(); } $results = array(); foreach ( $crons as $timestamp => $cronhooks ) { if ( $timestamp > $gmt_time ) { break; } $results[ $timestamp ] = $cronhooks; } return $results; } // // Private functions. // /** * Retrieve cron info array option. * * @since 2.1.0 * @access private * * @return array|false Cron info array on success, false on failure. */ function _get_cron_array() { $cron = get_option( 'cron' ); if ( ! is_array( $cron ) ) { return false; } if ( ! isset( $cron['version'] ) ) { $cron = _upgrade_cron_array( $cron ); } unset( $cron['version'] ); return $cron; } /** * Updates the cron option with the new cron array. * * @since 2.1.0 * @since 5.1.0 Return value modified to outcome of update_option(). * @since 5.7.0 The `$wp_error` parameter was added. * * @access private * * @param array $cron Cron info array from _get_cron_array(). * @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false. * @return bool|WP_Error True if cron array updated. False or WP_Error on failure. */ function _set_cron_array( $cron, $wp_error = false ) { $cron['version'] = 2; $result = update_option( 'cron', $cron ); if ( $wp_error && ! $result ) { return new WP_Error( 'could_not_set', __( 'The cron event list could not be saved.' ) ); } return $result; } /** * Upgrade a Cron info array. * * This function upgrades the Cron info array to version 2. * * @since 2.1.0 * @access private * * @param array $cron Cron info array from _get_cron_array(). * @return array An upgraded Cron info array. */ function _upgrade_cron_array( $cron ) { if ( isset( $cron['version'] ) && 2 == $cron['version'] ) { return $cron; } $new_cron = array(); foreach ( (array) $cron as $timestamp => $hooks ) { foreach ( (array) $hooks as $hook => $args ) { $key = md5( serialize( $args['args'] ) ); $new_cron[ $timestamp ][ $hook ][ $key ] = $args; } } $new_cron['version'] = 2; update_option( 'cron', $new_cron ); return $new_cron; }