get_param( 'cron_hash' ); if ( ! defined( 'DOING_CRON' ) ) { define( 'DOING_CRON', true ); } $result = self::execute_event( $cron_hash ); if ( ! $result ) { return new \WP_Error( 'cron_execute', __( 'Can not execute, cron not found. No longer exists maybe ? Try refreshing the page.', '0-day-analytics' ), array( 'status' => 400 ) ); } else { return \rest_ensure_response( array( 'success' => true, ) ); } } else { return new \WP_Error( 'cron_execute', __( 'Can not execute, request not provided.', '0-day-analytics' ), array( 'status' => 400 ) ); } } /** * Returns a cron event by its hash * * @param string $hash - The hash of the event to retrieve. * * @return array|bool * * @since 1.8.5 */ public static function get_event( string $hash ) { $events = self::get_events(); if ( isset( $events[ $hash ] ) ) { return $events[ $hash ]; } return false; } /** * Removes all cron events for a specific hook * * @param string $hook The action hook name. * * @since 1.3.0 */ public static function clear_events( $hook ) { \wp_clear_scheduled_hook( $hook ); } /** * Collects all cron events and formats them for inner use. * * @return array * * @since 1.3.0 */ public static function get_events() { if ( null === self::$events ) { $crons = _get_cron_array(); if ( $crons && is_array( $crons ) ) { if ( null === self::$events ) { self::$events = array(); } foreach ( $crons as $timestamp => $cron ) { if ( ! is_array( $cron ) ) { continue; } foreach ( $cron as $hook => $events ) { foreach ( $events as $event ) { $cron_item = array(); $cron_item['hook'] = \esc_html( $hook ); $cron_item['schedule'] = $timestamp; if ( isset( $event['schedule'] ) ) { $cron_item['recurrence'] = \esc_html( $event['schedule'] ); } if ( isset( $event['args'] ) ) { $cron_item['args'] = $event['args']; } $cron_item['hash'] = substr( md5( $cron_item['hook'] . $cron_item['recurrence'] . $cron_item['schedule'] . \wp_json_encode( $event['args'] ) ), 0, 8 ); // Site metadata for multisite clarity. $cron_item['site_id'] = function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 1; $cron_item['site_name'] = ( function_exists( 'get_bloginfo' ) ) ? get_bloginfo( 'name' ) : ''; // Derive all plugin slugs from callback files (multi-plugin aggregation). $callbacks = self::get_cron_callbacks( $hook ); $plugin_slugs = array(); if ( ! empty( $callbacks ) ) { foreach ( $callbacks as $cb ) { if ( isset( $cb['callback']['file'] ) && ! empty( $cb['callback']['file'] ) ) { $slug = Plugin_Theme_Helper::get_plugin_from_file_path( (string) $cb['callback']['file'] ); if ( false !== $slug && ! in_array( $slug, $plugin_slugs, true ) ) { $plugin_slugs[] = (string) $slug; } } } } // Backward compatibility single slug (first) & full list. $cron_item['plugin_slug'] = isset( $plugin_slugs[0] ) ? $plugin_slugs[0] : ''; $cron_item['plugin_slugs'] = $plugin_slugs; // empty array signifies core/unknown. self::$events[ $cron_item['hash'] ] = $cron_item; } } } } } return self::$events; } /** * Returns the cron events for a specific site in a multisite installation. * Safely switches to the blog, collects events, then restores. * * @param int $blog_id Site (blog) ID. * @return array */ public static function get_events_for_site( int $blog_id ): array { if ( ! function_exists( 'is_multisite' ) || ! is_multisite() ) { return self::get_events(); } $original = get_current_blog_id(); if ( $original === $blog_id ) { return self::get_events(); } // Switch, clear cache, collect, restore and clear again to avoid bleed. switch_to_blog( $blog_id ); self::$events = null; $events = self::get_events(); restore_current_blog(); self::$events = null; // ensure subsequent calls on original blog re-fetch. return $events; } /** * Clears the inner events cache. * * @since 1.3.0 */ private static function clear_inner_events(): void { self::$events = null; } /** * Determines whether an event is late. * * An event which has missed its schedule by more than 10 minutes is considered late. * * @param array $item - The item array which will be used for checking the time. * * @return bool Whether the event is late. * * @since 1.4.0 */ public static function is_late( array $item ) { if ( ! isset( $item['schedule'] ) && isset( $item['timestamp'] ) ) { $item['schedule'] = $item['timestamp']; } if ( ! isset( $item['schedule'] ) ) { return false; } $until = $item['schedule'] - time(); return ( $until < ( 0 - ( 10 * MINUTE_IN_SECONDS ) ) ); } /** * Shows select drop-down for cron schedules. * * @param boolean $current - Currently selected schedule. * * @return void * * @since 1.8.5 */ public static function schedule_drop_down( $current = false ) { $schedules = \wp_get_schedules(); uasort( $schedules, array( Crons_List::class, 'sort_schedules' ) ); ?> Crons_List::CRON_MENU_SLUG, Crons_List::SEARCH_INPUT => Crons_List::escaped_search_input(), 'updated' => false, 'cron_name' => rawurlencode( $cron['hook'] ), ), \admin_url( 'admin.php' ) ) ) ); exit; } $current_time = time(); $date = ( ( isset( $_REQUEST['cron_next_run_custom_date'] ) ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['cron_next_run_custom_date'] ) ) : '' ); $time = ( ( isset( $_REQUEST['cron_next_run_custom_time'] ) ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['cron_next_run_custom_time'] ) ) : '' ); $next_run_local = $date . ' ' . $time; $next_run_local = strtotime( $next_run_local, $current_time ); if ( false === $next_run_local ) { return new \WP_Error( 'invalid_timestamp', __( 'Invalid timestamp provided.', '0-day-analytics' ) ); } $next_run_utc = (int) \get_gmt_from_date( \gmdate( 'Y-m-d H:i:s', $next_run_local ), 'U' ); $schedule = ( isset( $_REQUEST['cron_schedule'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['cron_schedule'] ) ) : '' ); if ( '_oneoff' === $schedule ) { $schedule = ''; } elseif ( ! isset( \wp_get_schedules()[ $schedule ] ) ) { return new \WP_Error( 'invalid_schedule', __( 'Invalid schedule provided.', '0-day-analytics' ) ); } $args = ( isset( $_REQUEST['cron_args'] ) ? \sanitize_textarea_field( \wp_unslash( $_REQUEST['cron_args'] ) ) : '' ); $args = \json_decode( $args, true ); if ( empty( $args ) || ! is_array( $args ) ) { $args = array(); } $new_hook_name = ( isset( $_REQUEST['name'] ) ) ? $_REQUEST['name'] : ''; if ( empty( $new_hook_name ) ) { $new_hook_name = $cron['hook']; } if ( '_oneoff' === $schedule || '' === $schedule ) { $result = \wp_schedule_single_event( $next_run_utc, $new_hook_name, $args, true ); } else { $result = \wp_schedule_event( $next_run_utc, $schedule, $new_hook_name, $args, true ); } if ( \is_wp_error( $result ) ) { return new \WP_Error( 'invalid_cron_parameters', __( 'Cron job can not be added.', '0-day-analytics' ) ); } } /** * Adds a cron event. * * @return void|\WP_Error * * @since 1.8.5 */ public static function add_cron() { $current_time = time(); $date = ( ( isset( $_REQUEST['cron_next_run_custom_date'] ) ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['cron_next_run_custom_date'] ) ) : '' ); $time = ( ( isset( $_REQUEST['cron_next_run_custom_time'] ) ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['cron_next_run_custom_time'] ) ) : '' ); $next_run_local = $date . ' ' . $time; $next_run_local = strtotime( $next_run_local, $current_time ); if ( false === $next_run_local ) { return new \WP_Error( 'invalid_timestamp', __( 'Invalid timestamp provided.', '0-day-analytics' ) ); } $next_run_utc = (int) \get_gmt_from_date( \gmdate( 'Y-m-d H:i:s', $next_run_local ), 'U' ); $schedule = ( isset( $_REQUEST['cron_schedule'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['cron_schedule'] ) ) : '' ); if ( '_oneoff' === $schedule ) { $schedule = ''; } elseif ( ! isset( \wp_get_schedules()[ $schedule ] ) ) { return new \WP_Error( 'invalid_schedule', __( 'Invalid schedule provided.', '0-day-analytics' ) ); } $args = ( isset( $_REQUEST['cron_args'] ) ? \sanitize_textarea_field( \wp_unslash( $_REQUEST['cron_args'] ) ) : '' ); $args = \json_decode( $args, true ); if ( empty( $args ) || ! is_array( $args ) ) { $args = array(); } $new_hook_name = ( isset( $_REQUEST['name'] ) ) ? $_REQUEST['name'] : ''; if ( empty( $new_hook_name ) ) { $new_hook_name = 'unnamed_hook_' . uniqid(); } if ( '_oneoff' === $schedule || '' === $schedule ) { $result = \wp_schedule_single_event( $next_run_utc, $new_hook_name, $args, true ); } else { $result = \wp_schedule_event( $next_run_utc, $schedule, $new_hook_name, $args, true ); } if ( \is_wp_error( $result ) ) { return new \WP_Error( 'invalid_cron_parameters', __( 'Cron job can not be added.', '0-day-analytics' ) ); } } /** * Adds a cron job to the stored ones. * * @param array $cron_job - Array with the cron job information. Every cron job information includes 'time', 'hook', 'args', if it is a recurring one - and 'next_run'. * * Example: * 'hook_name' => array( * 'time' => 'monthly', * 'hook' => array( __CLASS_TO_CALL__, 'method_to_call' ), * 'args' => array(), * 'next_run' => '00:00 first day of next month', * ) * . * * @return void * * @throws \InvalidArgumentException When cron job information passed not contains required keys. * * @since 1.9.2 */ public static function add_cron_from_array( array $cron_job ) { } /** * Tests the proper spawning of the WP Cron * * @param boolean $cache - Flag - should only use the cached results from previous calls. * * @return bool|\WP_Error * * @since 1.8.5 */ public static function test_cron_spawn( $cache = true ) { global $wp_version; $cron_runner_plugins = array( '\HM\Cavalcade\Plugin\Job' => 'Cavalcade', '\Automattic\WP\Cron_Control\Main' => 'Cron Control', ); foreach ( $cron_runner_plugins as $class => $plugin ) { if ( class_exists( $class ) ) { return new \WP_Error( 'advana_cron_info', sprintf( /* translators: %s: The name of the plugin that controls the running of cron events. */ __( 'WP-Cron spawning is being managed by the %s plugin.', '0-day-analytics' ), $plugin ) ); } } if ( defined( 'DISABLE_WP_CRON' ) && \DISABLE_WP_CRON ) { return WP_Helper::check_cron_status(); } if ( defined( 'ALTERNATE_WP_CRON' ) && constant( 'ALTERNATE_WP_CRON' ) ) { return new \WP_Error( 'advana_cron_info', sprintf( /* translators: %s: The name of the PHP constant that is set. */ __( 'The %s constant is set to true.', '0-day-analytics' ), 'ALTERNATE_WP_CRON' ) ); } $cached_status = \get_transient( self::TRANSIENT_NAME ); if ( $cache && $cached_status ) { return true; } $sslverify = version_compare( $wp_version, '4.0', '<' ); $doing_wp_cron = sprintf( '%.22F', microtime( true ) ); $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' => 3, 'blocking' => true, 'sslverify' => apply_filters( 'https_local_ssl_verify', $sslverify ), ), ), $doing_wp_cron ); $cron_request['args']['blocking'] = true; $result = \wp_remote_post( $cron_request['url'], $cron_request['args'] ); if ( \is_wp_error( $result ) ) { return $result; } elseif ( \wp_remote_retrieve_response_code( $result ) >= 300 ) { return new \WP_Error( 'unexpected_http_response_code', sprintf( /* translators: %s: The HTTP response code. */ __( 'Unexpected HTTP response code: %s', '0-day-analytics' ), intval( \wp_remote_retrieve_response_code( $result ) ) ) ); } else { \set_transient( self::TRANSIENT_NAME, 1, 3600 ); return true; } } /** * Returns an array of the callback functions that are attached to the given hook name. * * @param string $name The hook name. * @return array> Array of callbacks attached to the hook. * @phpstan-return array, * }> * * @since 1.4.0 */ public static function get_cron_callbacks( $name ) { global $wp_filter; if ( $wp_filter && ! isset( $wp_filter[ $name ] ) ) { return array(); } $actions = array(); foreach ( $wp_filter[ $name ] as $priority => $callbacks ) { foreach ( $callbacks as $callback ) { $callback = self::populate_callback( $callback['function'] ); if ( __NAMESPACE__ . '\\pauser()' === $callback['name'] ) { continue; } $actions[] = array( 'priority' => $priority, 'callback' => $callback, ); } } return $actions; } /** * Tries to extract as much information as possible from a callback. * * @param array|string $callback - The callback to populate. * * @return array * @phpstan-return array{ * name?: string, * file?: string|false, * line?: string|false, * error?: WP_Error, * component?: QM_Component, * } * * @since 1.4.0 */ private static function populate_callback( $callback ): array { $result = array(); try { if ( is_array( $callback ) ) { if ( is_object( $callback[0] ) ) { $class = get_class( $callback[0] ); $access = '->'; } else { $class = $callback[0]; $access = '::'; } $result['name'] = WP_Helper::shorten_fqn( $class . $access . $callback[1] ) . '()'; $ref = new \ReflectionMethod( $class, $callback[1] ); } elseif ( is_object( $callback ) && method_exists( $callback, '__invoke' ) ) { $class = get_class( $callback ); $result['name'] = WP_Helper::shorten_fqn( $class ) . '->__invoke()'; $ref = new \ReflectionMethod( $class, '__invoke' ); } else { $function_name = is_string( $callback ) ? $callback : spl_object_hash( $callback ); $result['name'] = WP_Helper::shorten_fqn( $function_name ) . '()'; try { $ref = new \ReflectionFunction( $callback ); // Class as string ? } catch ( \ReflectionException $e ) { if ( PHP_VERSION_ID >= 80400 ) { $ref = \ReflectionMethod::createFromMethodName( $callback ); } else { $ref = new \ReflectionMethod( $callback ); } } } $result['file'] = $ref->getFileName(); $result['line'] = $ref->getStartLine(); $name = trim( $ref->getName() ); if ( '__lambda_func' === $name || 0 === strpos( $name, 'lambda_' ) ) { if ( $result['file'] && preg_match( '|(?P.*)\((?P[0-9]+)\)|', $result['file'], $matches ) ) { $result['file'] = $matches['file']; $result['line'] = $matches['line']; $file = trim( WP_Helper::standard_dir( $result['file'], '' ), '/' ); $result['name'] = sprintf( // translators: %1$d is the line number, %2$s is the file name. __( 'Anonymous function on line %1$d of %2$s', '0-day-analytics' ), $result['line'], $file ); } else { unset( $result['line'], $result['file'] ); $result['name'] = $name . '()'; $result['error'] = new \WP_Error( 'unknown_lambda', __( 'Unable to determine source of lambda function', '0-day-analytics' ) ); } } if ( ! empty( $result['file'] ) ) { $result['component'] = WP_Helper::get_file_component( $result['file'] ); } else { $result['component'] = array( 'type' => 'php', 'name' => 'PHP', 'context' => '', ); } } catch ( \ReflectionException $e ) { $result['error'] = new \WP_Error( 'reflection_exception', $e->getMessage() ); } return $result; } } }