[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> cron.php (source)

   1  <?php
   2  /**
   3   * WordPress Cron API
   4   *
   5   * @package WordPress
   6   */
   7  
   8  /**
   9   * Schedules an event to run only once.
  10   *
  11   * Schedules a hook which will be triggered by WordPress at the specified UTC time.
  12   * The action will trigger when someone visits your WordPress site if the scheduled
  13   * time has passed.
  14   *
  15   * Note that scheduling an event to occur within 10 minutes of an existing event
  16   * with the same action hook will be ignored unless you pass unique `$args` values
  17   * for each scheduled event.
  18   *
  19   * Use wp_next_scheduled() to prevent duplicate events.
  20   *
  21   * Use wp_schedule_event() to schedule a recurring event.
  22   *
  23   * @since 2.1.0
  24   * @since 5.1.0 Return value modified to boolean indicating success or failure,
  25   *              {@see 'pre_schedule_event'} filter added to short-circuit the function.
  26   * @since 5.7.0 The `$wp_error` parameter was added.
  27   *
  28   * @link https://developer.wordpress.org/reference/functions/wp_schedule_single_event/
  29   *
  30   * @param int    $timestamp  Unix timestamp (UTC) for when to next run the event.
  31   * @param string $hook       Action hook to execute when the event is run.
  32   * @param array  $args       Optional. Array containing arguments to pass to the
  33   *                           hook's callback function. Each value in the array
  34   *                           is passed to the callback as an individual parameter.
  35   *                           The array keys are ignored. Default empty array.
  36   *
  37   *                           These arguments are used to uniquely identify the
  38   *                           scheduled event and must match those used when the
  39   *                           event was originally scheduled. If the arguments
  40   *                           do not match exactly, WordPress will treat the
  41   *                           event as different, which can lead to duplicate
  42   *                           cron events being scheduled unintentionally,
  43   *                           excessive growth of the 'cron' option, and
  44   *                           database performance issues.
  45   * @param bool   $wp_error   Optional. Whether to return a WP_Error on failure. Default false.
  46   * @return bool|WP_Error True if event successfully scheduled. False or WP_Error on failure.
  47   *
  48   * @phpstan-return ( $wp_error is false ? bool : true|WP_Error )
  49   */
  50  function wp_schedule_single_event( $timestamp, $hook, $args = array(), $wp_error = false ) {
  51      // Make sure timestamp is a positive integer.
  52      if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
  53          if ( $wp_error ) {
  54              return new WP_Error(
  55                  'invalid_timestamp',
  56                  __( 'Event timestamp must be a valid Unix timestamp.' )
  57              );
  58          }
  59  
  60          return false;
  61      }
  62  
  63      $event = (object) array(
  64          'hook'      => $hook,
  65          'timestamp' => $timestamp,
  66          'schedule'  => false,
  67          'args'      => $args,
  68      );
  69  
  70      /**
  71       * Filter to override scheduling an event.
  72       *
  73       * Returning a non-null value will short-circuit adding the event to the
  74       * cron array, causing the function to return the filtered value instead.
  75       *
  76       * Both single events and recurring events are passed through this filter;
  77       * single events have `$event->schedule` as false, whereas recurring events
  78       * have this set to a recurrence from wp_get_schedules(). Recurring
  79       * events also have the integer recurrence interval set as `$event->interval`.
  80       *
  81       * For plugins replacing wp-cron, it is recommended you check for an
  82       * identical event within ten minutes and apply the {@see 'schedule_event'}
  83       * filter to check if another plugin has disallowed the event before scheduling.
  84       *
  85       * Return true if the event was scheduled, false or a WP_Error if not.
  86       *
  87       * @since 5.1.0
  88       * @since 5.7.0 The `$wp_error` parameter was added, and a WP_Error object can now be returned.
  89       *
  90       * @param null|bool|WP_Error $result   The value to return instead. Default null to continue adding the event.
  91       * @param object             $event    {
  92       *     An object containing an event's data.
  93       *
  94       *     @type string       $hook      Action hook to execute when the event is run.
  95       *     @type int          $timestamp Unix timestamp (UTC) for when to next run the event.
  96       *     @type string|false $schedule  How often the event should subsequently recur.
  97       *     @type array        $args      Array containing each separate argument to pass to the hook's callback function.
  98       *     @type int          $interval  Optional. The interval time in seconds for the schedule. Only present for recurring events.
  99       * }
 100       * @param bool               $wp_error Whether to return a WP_Error on failure.
 101       */
 102      $pre = apply_filters( 'pre_schedule_event', null, $event, $wp_error );
 103  
 104      if ( null !== $pre ) {
 105          if ( $wp_error && false === $pre ) {
 106              return new WP_Error(
 107                  'pre_schedule_event_false',
 108                  __( 'A plugin prevented the event from being scheduled.' )
 109              );
 110          }
 111  
 112          if ( ! $wp_error && is_wp_error( $pre ) ) {
 113              return false;
 114          }
 115  
 116          return $pre;
 117      }
 118  
 119      /*
 120       * Check for a duplicated event.
 121       *
 122       * Don't schedule an event if there's already an identical event
 123       * within 10 minutes.
 124       *
 125       * When scheduling events within ten minutes of the current time,
 126       * all past identical events are considered duplicates.
 127       *
 128       * When scheduling an event with a past timestamp (ie, before the
 129       * current time) all events scheduled within the next ten minutes
 130       * are considered duplicates.
 131       */
 132      $crons = _get_cron_array();
 133  
 134      $key       = md5( serialize( $event->args ) );
 135      $duplicate = false;
 136  
 137      if ( $event->timestamp < time() + 10 * MINUTE_IN_SECONDS ) {
 138          $min_timestamp = 0;
 139      } else {
 140          $min_timestamp = $event->timestamp - 10 * MINUTE_IN_SECONDS;
 141      }
 142  
 143      if ( $event->timestamp < time() ) {
 144          $max_timestamp = time() + 10 * MINUTE_IN_SECONDS;
 145      } else {
 146          $max_timestamp = $event->timestamp + 10 * MINUTE_IN_SECONDS;
 147      }
 148  
 149      foreach ( $crons as $event_timestamp => $cron ) {
 150          if ( $event_timestamp < $min_timestamp ) {
 151              continue;
 152          }
 153  
 154          if ( $event_timestamp > $max_timestamp ) {
 155              break;
 156          }
 157  
 158          if ( isset( $cron[ $event->hook ][ $key ] ) ) {
 159              $duplicate = true;
 160              break;
 161          }
 162      }
 163  
 164      if ( $duplicate ) {
 165          if ( $wp_error ) {
 166              return new WP_Error(
 167                  'duplicate_event',
 168                  __( 'A duplicate event already exists.' )
 169              );
 170          }
 171  
 172          return false;
 173      }
 174  
 175      /**
 176       * Modify an event before it is scheduled.
 177       *
 178       * @since 3.1.0
 179       *
 180       * @param object|false $event {
 181       *     An object containing an event's data, or boolean false to prevent the event from being scheduled.
 182       *
 183       *     @type string       $hook      Action hook to execute when the event is run.
 184       *     @type int          $timestamp Unix timestamp (UTC) for when to next run the event.
 185       *     @type string|false $schedule  How often the event should subsequently recur.
 186       *     @type array        $args      Array containing each separate argument to pass to the hook's callback function.
 187       *     @type int          $interval  Optional. The interval time in seconds for the schedule. Only present for recurring events.
 188       * }
 189       */
 190      $event = apply_filters( 'schedule_event', $event );
 191  
 192      // A plugin disallowed this event.
 193      if ( ! $event ) {
 194          if ( $wp_error ) {
 195              return new WP_Error(
 196                  'schedule_event_false',
 197                  __( 'A plugin disallowed this event.' )
 198              );
 199          }
 200  
 201          return false;
 202      }
 203  
 204      $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
 205          'schedule' => $event->schedule,
 206          'args'     => $event->args,
 207      );
 208  
 209      ksort( $crons );
 210  
 211      return _set_cron_array( $crons, $wp_error );
 212  }
 213  
 214  /**
 215   * Schedules a recurring event.
 216   *
 217   * Schedules a hook which will be triggered by WordPress at the specified interval.
 218   * The action will trigger when someone visits your WordPress site if the scheduled
 219   * time has passed.
 220   *
 221   * Valid values for the recurrence are 'hourly', 'twicedaily', 'daily', and 'weekly'.
 222   * These can be extended using the {@see 'cron_schedules'} filter in wp_get_schedules().
 223   *
 224   * Use wp_next_scheduled() to prevent duplicate events.
 225   *
 226   * Use wp_schedule_single_event() to schedule a non-recurring event.
 227   *
 228   * @since 2.1.0
 229   * @since 5.1.0 Return value modified to boolean indicating success or failure,
 230   *              {@see 'pre_schedule_event'} filter added to short-circuit the function.
 231   * @since 5.7.0 The `$wp_error` parameter was added.
 232   *
 233   * @link https://developer.wordpress.org/reference/functions/wp_schedule_event/
 234   *
 235   * @param int    $timestamp  Unix timestamp (UTC) for when to next run the event.
 236   * @param string $recurrence How often the event should subsequently recur.
 237   *                           See wp_get_schedules() for accepted values.
 238   * @param string $hook       Action hook to execute when the event is run.
 239   * @param array  $args       Optional. Array containing arguments to pass to the
 240   *                           hook's callback function. Each value in the array
 241   *                           is passed to the callback as an individual parameter.
 242   *                           The array keys are ignored. Default empty array.
 243   *
 244   *                           These arguments are used to uniquely identify the
 245   *                           scheduled event and must match those used when the
 246   *                           event was originally scheduled. If the arguments
 247   *                           do not match exactly, WordPress will treat the
 248   *                           event as different, which can lead to duplicate
 249   *                           cron events being scheduled unintentionally,
 250   *                           excessive growth of the 'cron' option, and
 251   *                           database performance issues.
 252   * @param bool   $wp_error   Optional. Whether to return a WP_Error on failure. Default false.
 253   * @return bool|WP_Error True if event successfully scheduled. False or WP_Error on failure.
 254   *
 255   * @phpstan-return ( $wp_error is false ? bool : true|WP_Error )
 256   */
 257  function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array(), $wp_error = false ) {
 258      // Make sure timestamp is a positive integer.
 259      if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 260          if ( $wp_error ) {
 261              return new WP_Error(
 262                  'invalid_timestamp',
 263                  __( 'Event timestamp must be a valid Unix timestamp.' )
 264              );
 265          }
 266  
 267          return false;
 268      }
 269  
 270      $schedules = wp_get_schedules();
 271  
 272      if ( ! isset( $schedules[ $recurrence ] ) ) {
 273          if ( $wp_error ) {
 274              return new WP_Error(
 275                  'invalid_schedule',
 276                  __( 'Event schedule does not exist.' )
 277              );
 278          }
 279  
 280          return false;
 281      }
 282  
 283      $event = (object) array(
 284          'hook'      => $hook,
 285          'timestamp' => $timestamp,
 286          'schedule'  => $recurrence,
 287          'args'      => $args,
 288          'interval'  => $schedules[ $recurrence ]['interval'],
 289      );
 290  
 291      /** This filter is documented in wp-includes/cron.php */
 292      $pre = apply_filters( 'pre_schedule_event', null, $event, $wp_error );
 293  
 294      if ( null !== $pre ) {
 295          if ( $wp_error && false === $pre ) {
 296              return new WP_Error(
 297                  'pre_schedule_event_false',
 298                  __( 'A plugin prevented the event from being scheduled.' )
 299              );
 300          }
 301  
 302          if ( ! $wp_error && is_wp_error( $pre ) ) {
 303              return false;
 304          }
 305  
 306          return $pre;
 307      }
 308  
 309      /** This filter is documented in wp-includes/cron.php */
 310      $event = apply_filters( 'schedule_event', $event );
 311  
 312      // A plugin disallowed this event.
 313      if ( ! $event ) {
 314          if ( $wp_error ) {
 315              return new WP_Error(
 316                  'schedule_event_false',
 317                  __( 'A plugin disallowed this event.' )
 318              );
 319          }
 320  
 321          return false;
 322      }
 323  
 324      $key = md5( serialize( $event->args ) );
 325  
 326      $crons = _get_cron_array();
 327  
 328      $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
 329          'schedule' => $event->schedule,
 330          'args'     => $event->args,
 331          'interval' => $event->interval,
 332      );
 333  
 334      ksort( $crons );
 335  
 336      return _set_cron_array( $crons, $wp_error );
 337  }
 338  
 339  /**
 340   * Reschedules a recurring event.
 341   *
 342   * Mainly for internal use, this takes the Unix timestamp (UTC) of a previously run
 343   * recurring event and reschedules it for its next run.
 344   *
 345   * To change upcoming scheduled events, use wp_schedule_event() to
 346   * change the recurrence frequency.
 347   *
 348   * @since 2.1.0
 349   * @since 5.1.0 Return value modified to boolean indicating success or failure,
 350   *              {@see 'pre_reschedule_event'} filter added to short-circuit the function.
 351   * @since 5.7.0 The `$wp_error` parameter was added.
 352   *
 353   * @param int    $timestamp  Unix timestamp (UTC) for when the event was scheduled.
 354   * @param string $recurrence How often the event should subsequently recur.
 355   *                           See wp_get_schedules() for accepted values.
 356   * @param string $hook       Action hook to execute when the event is run.
 357   * @param array  $args       Optional. Array containing arguments to pass to the
 358   *                           hook's callback function. Each value in the array
 359   *                           is passed to the callback as an individual parameter.
 360   *                           The array keys are ignored. Default empty array.
 361   *
 362   *                           These arguments are used to uniquely identify the
 363   *                           scheduled event and must match those used when the
 364   *                           event was originally scheduled. If the arguments
 365   *                           do not match exactly, WordPress will treat the
 366   *                           event as different, which can lead to duplicate
 367   *                           cron events being scheduled unintentionally,
 368   *                           excessive growth of the 'cron' option, and
 369   *                           database performance issues.
 370   * @param bool   $wp_error   Optional. Whether to return a WP_Error on failure. Default false.
 371   * @return bool|WP_Error True if event successfully rescheduled. False or WP_Error on failure.
 372   *
 373   * @phpstan-return ( $wp_error is false ? bool : true|WP_Error )
 374   */
 375  function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array(), $wp_error = false ) {
 376      // Make sure timestamp is a positive integer.
 377      if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 378          if ( $wp_error ) {
 379              return new WP_Error(
 380                  'invalid_timestamp',
 381                  __( 'Event timestamp must be a valid Unix timestamp.' )
 382              );
 383          }
 384  
 385          return false;
 386      }
 387  
 388      $schedules = wp_get_schedules();
 389      $interval  = 0;
 390  
 391      // First we try to get the interval from the schedule.
 392      if ( isset( $schedules[ $recurrence ] ) ) {
 393          $interval = $schedules[ $recurrence ]['interval'];
 394      }
 395  
 396      // Now we try to get it from the saved interval in case the schedule disappears.
 397      if ( 0 === $interval ) {
 398          $scheduled_event = wp_get_scheduled_event( $hook, $args, $timestamp );
 399  
 400          if ( $scheduled_event && isset( $scheduled_event->interval ) ) {
 401              $interval = $scheduled_event->interval;
 402          }
 403      }
 404  
 405      $event = (object) array(
 406          'hook'      => $hook,
 407          'timestamp' => $timestamp,
 408          'schedule'  => $recurrence,
 409          'args'      => $args,
 410          'interval'  => $interval,
 411      );
 412  
 413      /**
 414       * Filter to override rescheduling of a recurring event.
 415       *
 416       * Returning a non-null value will short-circuit the normal rescheduling
 417       * process, causing the function to return the filtered value instead.
 418       *
 419       * For plugins replacing wp-cron, return true if the event was successfully
 420       * rescheduled, false or a WP_Error if not.
 421       *
 422       * @since 5.1.0
 423       * @since 5.7.0 The `$wp_error` parameter was added, and a WP_Error object can now be returned.
 424       *
 425       * @param null|bool|WP_Error $pre      Value to return instead. Default null to continue adding the event.
 426       * @param object             $event    {
 427       *     An object containing an event's data.
 428       *
 429       *     @type string $hook      Action hook to execute when the event is run.
 430       *     @type int    $timestamp Unix timestamp (UTC) for when to next run the event.
 431       *     @type string $schedule  How often the event should subsequently recur.
 432       *     @type array  $args      Array containing each separate argument to pass to the hook's callback function.
 433       *     @type int    $interval  The interval time in seconds for the schedule.
 434       * }
 435       * @param bool               $wp_error Whether to return a WP_Error on failure.
 436       */
 437      $pre = apply_filters( 'pre_reschedule_event', null, $event, $wp_error );
 438  
 439      if ( null !== $pre ) {
 440          if ( $wp_error && false === $pre ) {
 441              return new WP_Error(
 442                  'pre_reschedule_event_false',
 443                  __( 'A plugin prevented the event from being rescheduled.' )
 444              );
 445          }
 446  
 447          if ( ! $wp_error && is_wp_error( $pre ) ) {
 448              return false;
 449          }
 450  
 451          return $pre;
 452      }
 453  
 454      // Now we assume something is wrong and fail to schedule.
 455      if ( 0 === $interval ) {
 456          if ( $wp_error ) {
 457              return new WP_Error(
 458                  'invalid_schedule',
 459                  __( 'Event schedule does not exist.' )
 460              );
 461          }
 462  
 463          return false;
 464      }
 465  
 466      $now = time();
 467  
 468      if ( $timestamp >= $now ) {
 469          $timestamp = $now + $interval;
 470      } else {
 471          $timestamp = $now + ( $interval - ( ( $now - $timestamp ) % $interval ) );
 472      }
 473  
 474      return wp_schedule_event( $timestamp, $recurrence, $hook, $args, $wp_error );
 475  }
 476  
 477  /**
 478   * Unschedules a previously scheduled event.
 479   *
 480   * The `$timestamp` and `$hook` parameters are required so that the event can be
 481   * identified.
 482   *
 483   * @since 2.1.0
 484   * @since 5.1.0 Return value modified to boolean indicating success or failure,
 485   *              {@see 'pre_unschedule_event'} filter added to short-circuit the function.
 486   * @since 5.7.0 The `$wp_error` parameter was added.
 487   *
 488   * @param int    $timestamp Unix timestamp (UTC) of the event.
 489   * @param string $hook      Action hook of the event.
 490   * @param array  $args      Optional. Array containing each separate argument to pass to the hook's callback function.
 491   *                          Although not passed to a callback, these arguments are used to uniquely identify the
 492   *                          event, so they must match those used when originally scheduling the event. If the
 493   *                          arguments do not match exactly, the event will not be found. Default empty array.
 494   * @param bool   $wp_error  Optional. Whether to return a WP_Error on failure. Default false.
 495   * @return bool|WP_Error True if event successfully unscheduled. False or WP_Error on failure.
 496   *
 497   * @phpstan-return ( $wp_error is false ? bool : true|WP_Error )
 498   */
 499  function wp_unschedule_event( $timestamp, $hook, $args = array(), $wp_error = false ) {
 500      // Make sure timestamp is a positive integer.
 501      if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 502          if ( $wp_error ) {
 503              return new WP_Error(
 504                  'invalid_timestamp',
 505                  __( 'Event timestamp must be a valid Unix timestamp.' )
 506              );
 507          }
 508  
 509          return false;
 510      }
 511  
 512      /**
 513       * Filter to override unscheduling of events.
 514       *
 515       * Returning a non-null value will short-circuit the normal unscheduling
 516       * process, causing the function to return the filtered value instead.
 517       *
 518       * For plugins replacing wp-cron, return true if the event was successfully
 519       * unscheduled, false or a WP_Error if not.
 520       *
 521       * @since 5.1.0
 522       * @since 5.7.0 The `$wp_error` parameter was added, and a WP_Error object can now be returned.
 523       *
 524       * @param null|bool|WP_Error $pre       Value to return instead. Default null to continue unscheduling the event.
 525       * @param int                $timestamp Unix timestamp (UTC) for when to run the event.
 526       * @param string             $hook      Action hook, the execution of which will be unscheduled.
 527       * @param array              $args      Arguments to pass to the hook's callback function.
 528       * @param bool               $wp_error  Whether to return a WP_Error on failure.
 529       */
 530      $pre = apply_filters( 'pre_unschedule_event', null, $timestamp, $hook, $args, $wp_error );
 531  
 532      if ( null !== $pre ) {
 533          if ( $wp_error && false === $pre ) {
 534              return new WP_Error(
 535                  'pre_unschedule_event_false',
 536                  __( 'A plugin prevented the event from being unscheduled.' )
 537              );
 538          }
 539  
 540          if ( ! $wp_error && is_wp_error( $pre ) ) {
 541              return false;
 542          }
 543  
 544          return $pre;
 545      }
 546  
 547      $crons = _get_cron_array();
 548      $key   = md5( serialize( $args ) );
 549  
 550      unset( $crons[ $timestamp ][ $hook ][ $key ] );
 551  
 552      if ( empty( $crons[ $timestamp ][ $hook ] ) ) {
 553          unset( $crons[ $timestamp ][ $hook ] );
 554      }
 555  
 556      if ( empty( $crons[ $timestamp ] ) ) {
 557          unset( $crons[ $timestamp ] );
 558      }
 559  
 560      return _set_cron_array( $crons, $wp_error );
 561  }
 562  
 563  /**
 564   * Unschedules all events attached to the hook with the specified arguments.
 565   *
 566   * Warning: This function may return boolean false, but may also return a non-boolean
 567   * value which evaluates to false. For information about casting to booleans see the
 568   * {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
 569   * the `===` operator for testing the return value of this function.
 570   *
 571   * @since 2.1.0
 572   * @since 5.1.0 Return value modified to indicate success or failure,
 573   *              {@see 'pre_clear_scheduled_hook'} filter added to short-circuit the function.
 574   * @since 5.7.0 The `$wp_error` parameter was added.
 575   *
 576   * @param string $hook     Action hook, the execution of which will be unscheduled.
 577   * @param array  $args     Optional. Array containing each separate argument to pass to the hook's callback function.
 578   *                         Although not passed to a callback, these arguments are used to uniquely identify the
 579   *                         event, so they must match those used when originally scheduling the event. If the
 580   *                         arguments do not match exactly, the event will not be found. Default empty array.
 581   * @param bool   $wp_error Optional. Whether to return a WP_Error on failure. Default false.
 582   * @return int|false|WP_Error On success an integer indicating number of events unscheduled (0 indicates no
 583   *                            events were registered with the hook and arguments combination), false or WP_Error
 584   *                            if unscheduling one or more events fail.
 585   *
 586   * @phpstan-return ( int|( $wp_error is false ? false : WP_Error ) )
 587   */
 588  function wp_clear_scheduled_hook( $hook, $args = array(), $wp_error = false ) {
 589      /*
 590       * Backward compatibility.
 591       * Previously, this function took the arguments as discrete vars rather than an array like the rest of the API.
 592       */
 593      if ( ! is_array( $args ) ) {
 594          _deprecated_argument(
 595              __FUNCTION__,
 596              '3.0.0',
 597              __( 'This argument has changed to an array to match the behavior of the other cron functions.' )
 598          );
 599  
 600          $args     = array_slice( func_get_args(), 1 ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
 601          $wp_error = false;
 602      }
 603  
 604      /**
 605       * Filter to override clearing a scheduled hook.
 606       *
 607       * Returning a non-null value will short-circuit the normal unscheduling
 608       * process, causing the function to return the filtered value instead.
 609       *
 610       * For plugins replacing wp-cron, return the number of events successfully
 611       * unscheduled (zero if no events were registered with the hook) or false
 612       * or a WP_Error if unscheduling one or more events fails.
 613       *
 614       * @since 5.1.0
 615       * @since 5.7.0 The `$wp_error` parameter was added, and a WP_Error object can now be returned.
 616       *
 617       * @param null|int|false|WP_Error $pre      Value to return instead. Default null to continue unscheduling the event.
 618       * @param string                  $hook     Action hook, the execution of which will be unscheduled.
 619       * @param array                   $args     Arguments to pass to the hook's callback function.
 620       * @param bool                    $wp_error Whether to return a WP_Error on failure.
 621       */
 622      $pre = apply_filters( 'pre_clear_scheduled_hook', null, $hook, $args, $wp_error );
 623  
 624      if ( null !== $pre ) {
 625          if ( $wp_error && false === $pre ) {
 626              return new WP_Error(
 627                  'pre_clear_scheduled_hook_false',
 628                  __( 'A plugin prevented the hook from being cleared.' )
 629              );
 630          }
 631  
 632          if ( ! $wp_error && is_wp_error( $pre ) ) {
 633              return false;
 634          }
 635  
 636          return $pre;
 637      }
 638  
 639      /*
 640       * This logic duplicates wp_next_scheduled().
 641       * It's required due to a scenario where wp_unschedule_event() fails due to update_option() failing,
 642       * and, wp_next_scheduled() returns the same schedule in an infinite loop.
 643       */
 644      $crons = _get_cron_array();
 645      if ( empty( $crons ) ) {
 646          return 0;
 647      }
 648  
 649      $results = array();
 650      $key     = md5( serialize( $args ) );
 651  
 652      foreach ( $crons as $timestamp => $cron ) {
 653          if ( isset( $cron[ $hook ][ $key ] ) ) {
 654              $results[] = wp_unschedule_event( $timestamp, $hook, $args, true );
 655          }
 656      }
 657  
 658      $errors = array_filter( $results, 'is_wp_error' );
 659      $error  = new WP_Error();
 660  
 661      if ( $errors ) {
 662          if ( $wp_error ) {
 663              array_walk( $errors, array( $error, 'merge_from' ) );
 664  
 665              return $error;
 666          }
 667  
 668          return false;
 669      }
 670  
 671      return count( $results );
 672  }
 673  
 674  /**
 675   * Unschedules all events attached to the hook.
 676   *
 677   * Can be useful for plugins when deactivating to clean up the cron queue.
 678   *
 679   * Warning: This function may return boolean false, but may also return a non-boolean
 680   * value which evaluates to false. For information about casting to booleans see the
 681   * {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
 682   * the `===` operator for testing the return value of this function.
 683   *
 684   * @since 4.9.0
 685   * @since 5.1.0 Return value added to indicate success or failure.
 686   * @since 5.7.0 The `$wp_error` parameter was added.
 687   *
 688   * @param string $hook     Action hook, the execution of which will be unscheduled.
 689   * @param bool   $wp_error Optional. Whether to return a WP_Error on failure. Default false.
 690   * @return int|false|WP_Error On success an integer indicating number of events unscheduled (0 indicates no
 691   *                            events were registered on the hook), false or WP_Error if unscheduling fails.
 692   *
 693   * @phpstan-return ( $wp_error is false ? int|false : int|WP_Error )
 694   */
 695  function wp_unschedule_hook( $hook, $wp_error = false ) {
 696      /**
 697       * Filter to override clearing all events attached to the hook.
 698       *
 699       * Returning a non-null value will short-circuit the normal unscheduling
 700       * process, causing the function to return the filtered value instead.
 701       *
 702       * For plugins replacing wp-cron, return the number of events successfully
 703       * unscheduled (zero if no events were registered with the hook). If unscheduling
 704       * one or more events fails then return either a WP_Error object or false depending
 705       * on the value of the `$wp_error` parameter.
 706       *
 707       * @since 5.1.0
 708       * @since 5.7.0 The `$wp_error` parameter was added, and a WP_Error object can now be returned.
 709       *
 710       * @param null|int|false|WP_Error $pre      Value to return instead. Default null to continue unscheduling the hook.
 711       * @param string                  $hook     Action hook, the execution of which will be unscheduled.
 712       * @param bool                    $wp_error Whether to return a WP_Error on failure.
 713       */
 714      $pre = apply_filters( 'pre_unschedule_hook', null, $hook, $wp_error );
 715  
 716      if ( null !== $pre ) {
 717          if ( $wp_error && false === $pre ) {
 718              return new WP_Error(
 719                  'pre_unschedule_hook_false',
 720                  __( 'A plugin prevented the hook from being cleared.' )
 721              );
 722          }
 723  
 724          if ( ! $wp_error && is_wp_error( $pre ) ) {
 725              return false;
 726          }
 727  
 728          return $pre;
 729      }
 730  
 731      $crons = _get_cron_array();
 732      if ( empty( $crons ) ) {
 733          return 0;
 734      }
 735  
 736      $results = array();
 737  
 738      foreach ( $crons as $timestamp => $args ) {
 739          if ( ! empty( $crons[ $timestamp ][ $hook ] ) ) {
 740              $results[] = count( $crons[ $timestamp ][ $hook ] );
 741          }
 742  
 743          unset( $crons[ $timestamp ][ $hook ] );
 744  
 745          if ( empty( $crons[ $timestamp ] ) ) {
 746              unset( $crons[ $timestamp ] );
 747          }
 748      }
 749  
 750      /*
 751       * If the results are empty (zero events to unschedule), no attempt
 752       * to update the cron array is required.
 753       */
 754      if ( empty( $results ) ) {
 755          return 0;
 756      }
 757  
 758      $set = _set_cron_array( $crons, $wp_error );
 759  
 760      if ( true === $set ) {
 761          return array_sum( $results );
 762      }
 763  
 764      return $set;
 765  }
 766  
 767  /**
 768   * Retrieves a scheduled event.
 769   *
 770   * Retrieves the full event object for a given event, if no timestamp is specified the next
 771   * scheduled event is returned.
 772   *
 773   * @since 5.1.0
 774   *
 775   * @param string   $hook      Action hook of the event.
 776   * @param array    $args      Optional. Array containing each separate argument to pass to the hook's callback function.
 777   *                            Although not passed to a callback, these arguments are used to uniquely identify the
 778   *                            event, so they should be the same as those used when originally scheduling the event.
 779   *                            Default empty array.
 780   * @param int|null $timestamp Optional. Unix timestamp (UTC) of the event. If not specified, the next scheduled event
 781   *                            is returned. Default null.
 782   * @return stdClass|false {
 783   *     The event object. False if the event does not exist.
 784   *
 785   *     @type string       $hook      Action hook to execute when the event is run.
 786   *     @type int          $timestamp Unix timestamp (UTC) for when to next run the event.
 787   *     @type string|false $schedule  How often the event should subsequently recur.
 788   *     @type array        $args      Array containing each separate argument to pass to the hook's callback function.
 789   *     @type int          $interval  Optional. The interval time in seconds for the schedule. Only present for recurring events.
 790   * }
 791   */
 792  function wp_get_scheduled_event( $hook, $args = array(), $timestamp = null ) {
 793      /**
 794       * Filter to override retrieving a scheduled event.
 795       *
 796       * Returning a non-null value will short-circuit the normal process,
 797       * returning the filtered value instead.
 798       *
 799       * Return false if the event does not exist, otherwise an event object
 800       * should be returned.
 801       *
 802       * @since 5.1.0
 803       *
 804       * @param null|false|object $pre       Value to return instead. Default null to continue retrieving the event.
 805       * @param string            $hook      Action hook of the event.
 806       * @param array             $args      Array containing each separate argument to pass to the hook's callback function.
 807       *                                     Although not passed to a callback, these arguments are used to uniquely identify
 808       *                                     the event.
 809       * @param int|null          $timestamp Unix timestamp (UTC) of the event. Null to retrieve next scheduled event.
 810       */
 811      $pre = apply_filters( 'pre_get_scheduled_event', null, $hook, $args, $timestamp );
 812  
 813      if ( null !== $pre ) {
 814          return $pre;
 815      }
 816  
 817      if ( null !== $timestamp && ! is_numeric( $timestamp ) ) {
 818          return false;
 819      }
 820  
 821      $crons = _get_cron_array();
 822      if ( empty( $crons ) ) {
 823          return false;
 824      }
 825  
 826      $key = md5( serialize( $args ) );
 827  
 828      if ( ! $timestamp ) {
 829          // Get next event.
 830          $next = false;
 831          foreach ( $crons as $timestamp => $cron ) {
 832              if ( isset( $cron[ $hook ][ $key ] ) ) {
 833                  $next = $timestamp;
 834                  break;
 835              }
 836          }
 837  
 838          if ( ! $next ) {
 839              return false;
 840          }
 841  
 842          $timestamp = $next;
 843      } elseif ( ! isset( $crons[ $timestamp ][ $hook ][ $key ] ) ) {
 844          return false;
 845      }
 846  
 847      $event = (object) array(
 848          'hook'      => $hook,
 849          'timestamp' => $timestamp,
 850          'schedule'  => $crons[ $timestamp ][ $hook ][ $key ]['schedule'],
 851          'args'      => $args,
 852      );
 853  
 854      if ( isset( $crons[ $timestamp ][ $hook ][ $key ]['interval'] ) ) {
 855          $event->interval = $crons[ $timestamp ][ $hook ][ $key ]['interval'];
 856      }
 857  
 858      return $event;
 859  }
 860  
 861  /**
 862   * Retrieves the timestamp of the next scheduled event for the given hook.
 863   *
 864   * @since 2.1.0
 865   *
 866   * @param string $hook Action hook of the event.
 867   * @param array  $args Optional. Array containing each separate argument to pass to the hook's callback function.
 868   *                     Although not passed to a callback, these arguments are used to uniquely identify the
 869   *                     event, so they must match those used when originally scheduling the event. If the
 870   *                     arguments do not match exactly, the event will not be found. Default empty array.
 871   * @return int|false The Unix timestamp (UTC) of the next time the event will occur. False if the event doesn't exist.
 872   */
 873  function wp_next_scheduled( $hook, $args = array() ) {
 874      $next_event = wp_get_scheduled_event( $hook, $args );
 875  
 876      if ( ! $next_event ) {
 877          return false;
 878      }
 879  
 880      /**
 881       * Filters the timestamp of the next scheduled event for the given hook.
 882       *
 883       * @since 6.8.0
 884       *
 885       * @param int    $timestamp  Unix timestamp (UTC) for when to next run the event.
 886       * @param object $next_event {
 887       *     An object containing an event's data.
 888       *
 889       *     @type string $hook      Action hook of the event.
 890       *     @type int    $timestamp Unix timestamp (UTC) for when to next run the event.
 891       *     @type string $schedule  How often the event should subsequently recur.
 892       *     @type array  $args      Array containing each separate argument to pass to the hook
 893       *                             callback function.
 894       *     @type int    $interval  Optional. The interval time in seconds for the schedule. Only
 895       *                             present for recurring events.
 896       * }
 897       * @param string $hook       Action hook of the event.
 898       * @param array  $args       Array containing each separate argument to pass to the hook
 899       *                           callback function.
 900       */
 901      return apply_filters( 'wp_next_scheduled', $next_event->timestamp, $next_event, $hook, $args );
 902  }
 903  
 904  /**
 905   * Sends a request to run cron through HTTP request that doesn't halt page loading.
 906   *
 907   * @since 2.1.0
 908   * @since 5.1.0 Return values added.
 909   *
 910   * @param int $gmt_time Optional. Unix timestamp (UTC). Default 0 (current time is used).
 911   * @return bool True if spawned, false if no events spawned.
 912   */
 913  function spawn_cron( $gmt_time = 0 ) {
 914      if ( ! $gmt_time ) {
 915          $gmt_time = microtime( true );
 916      }
 917  
 918      if ( defined( 'DOING_CRON' ) || isset( $_GET['doing_wp_cron'] ) ) {
 919          return false;
 920      }
 921  
 922      /*
 923       * Get the cron lock, which is a Unix timestamp of when the last cron was spawned
 924       * and has not finished running.
 925       *
 926       * Multiple processes on multiple web servers can run this code concurrently,
 927       * this lock attempts to make spawning as atomic as possible.
 928       */
 929      $lock = (float) get_transient( 'doing_cron' );
 930  
 931      if ( $lock > $gmt_time + 10 * MINUTE_IN_SECONDS ) {
 932          $lock = 0;
 933      }
 934  
 935      // Don't run if another process is currently running it or more than once every 60 sec.
 936      if ( $lock + WP_CRON_LOCK_TIMEOUT > $gmt_time ) {
 937          return false;
 938      }
 939  
 940      // Confidence check.
 941      $crons = wp_get_ready_cron_jobs();
 942      if ( empty( $crons ) ) {
 943          return false;
 944      }
 945  
 946      if ( array_key_first( $crons ) > $gmt_time ) {
 947          return false;
 948      }
 949  
 950      if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
 951          if ( 'GET' !== $_SERVER['REQUEST_METHOD'] || defined( 'DOING_AJAX' ) || defined( 'XMLRPC_REQUEST' ) ) {
 952              return false;
 953          }
 954  
 955          $doing_wp_cron = sprintf( '%.22F', $gmt_time );
 956          set_transient( 'doing_cron', $doing_wp_cron );
 957  
 958          ob_start();
 959          wp_redirect( add_query_arg( 'doing_wp_cron', $doing_wp_cron, wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
 960          echo ' ';
 961  
 962          // Flush any buffers and send the headers.
 963          wp_ob_end_flush_all();
 964          flush();
 965  
 966          require_once  ABSPATH . 'wp-cron.php';
 967          return true;
 968      }
 969  
 970      // Set the cron lock with the current unix timestamp, when the cron is being spawned.
 971      $doing_wp_cron = sprintf( '%.22F', $gmt_time );
 972      set_transient( 'doing_cron', $doing_wp_cron );
 973  
 974      $cron_url = add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) );
 975  
 976      /**
 977       * Filters the cron request arguments.
 978       *
 979       * @since 3.5.0
 980       * @since 4.5.0 The `$doing_wp_cron` parameter was added.
 981       *
 982       * @param array  $cron_request_array {
 983       *     An array of cron request URL arguments.
 984       *
 985       *     @type string $url  The cron request URL.
 986       *     @type string $key  The Unix timestamp (UTC) of the cron lock with microseconds.
 987       *     @type array  $args {
 988       *         An array of cron request arguments.
 989       *
 990       *         @type int  $timeout   The request timeout in seconds. Default .01 seconds.
 991       *         @type bool $blocking  Whether to set blocking for the request. Default false.
 992       *         @type bool $sslverify Whether SSL should be verified for the request. Default false.
 993       *     }
 994       * }
 995       * @param string $doing_wp_cron      The Unix timestamp (UTC) of the cron lock with microseconds.
 996       */
 997      $cron_request = apply_filters(
 998          'cron_request',
 999          array(
1000              'url'  => $cron_url,
1001              'key'  => $doing_wp_cron,
1002              'args' => array(
1003                  'timeout'   => 0.01,
1004                  'blocking'  => false,
1005                  /** This filter is documented in wp-includes/class-wp-http-streams.php */
1006                  'sslverify' => apply_filters( 'https_local_ssl_verify', false, $cron_url ),
1007              ),
1008          ),
1009          $doing_wp_cron
1010      );
1011  
1012      $result = wp_remote_post( $cron_request['url'], $cron_request['args'] );
1013  
1014      return ! is_wp_error( $result );
1015  }
1016  
1017  /**
1018   * Registers _wp_cron() to run on the {@see 'shutdown'} action.
1019   *
1020   * The spawn_cron() function attempts to make a non-blocking loopback request to `wp-cron.php` (when alternative
1021   * cron is not being used). However, the wp_remote_post() function does not always respect the `timeout` and
1022   * `blocking` parameters. A timeout of `0.01` may end up taking 1 second. When this runs at the {@see 'wp_loaded'}
1023   * action, it increases the Time To First Byte (TTFB) since the HTML cannot be sent while waiting for the cron request
1024   * to initiate. Moving the spawning of cron to the {@see 'shutdown'} hook allows for the server to flush the HTML document to
1025   * the browser while waiting for the request.
1026   *
1027   * @since 2.1.0
1028   * @since 5.1.0 Return value added to indicate success or failure.
1029   * @since 5.7.0 Functionality moved to _wp_cron() to which this becomes a wrapper.
1030   * @since 6.9.0 The _wp_cron() callback is moved from {@see 'wp_loaded'} to the {@see 'shutdown'} action,
1031   *              unless `ALTERNATE_WP_CRON` is enabled; the function now always returns void.
1032   */
1033  function wp_cron(): void {
1034      if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
1035          if ( did_action( 'wp_loaded' ) ) {
1036              _wp_cron();
1037          } else {
1038              add_action( 'wp_loaded', '_wp_cron', 20 );
1039          }
1040      } elseif ( doing_action( 'shutdown' ) ) {
1041          _wp_cron();
1042      } else {
1043          add_action( 'shutdown', '_wp_cron' );
1044      }
1045  }
1046  
1047  /**
1048   * Runs scheduled callbacks or spawns cron for all scheduled events.
1049   *
1050   * Warning: This function may return Boolean FALSE, but may also return a non-Boolean
1051   * value which evaluates to FALSE. For information about casting to booleans see the
1052   * {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
1053   * the `===` operator for testing the return value of this function.
1054   *
1055   * @since 5.7.0
1056   * @access private
1057   *
1058   * @return int|false On success an integer indicating number of events spawned (0 indicates no
1059   *                   events needed to be spawned), false if spawning fails for one or more events.
1060   */
1061  function _wp_cron() {
1062      // Prevent infinite loops caused by lack of wp-cron.php.
1063      if ( str_contains( $_SERVER['REQUEST_URI'], '/wp-cron.php' )
1064          || ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON )
1065      ) {
1066          return 0;
1067      }
1068  
1069      $crons = wp_get_ready_cron_jobs();
1070      if ( empty( $crons ) ) {
1071          return 0;
1072      }
1073  
1074      $gmt_time = microtime( true );
1075      if ( array_key_first( $crons ) > $gmt_time ) {
1076          return 0;
1077      }
1078  
1079      $schedules = wp_get_schedules();
1080      $results   = array();
1081  
1082      foreach ( $crons as $timestamp => $cronhooks ) {
1083          if ( $timestamp > $gmt_time ) {
1084              break;
1085          }
1086  
1087          foreach ( (array) $cronhooks as $hook => $args ) {
1088              if ( isset( $schedules[ $hook ]['callback'] )
1089                  && ! call_user_func( $schedules[ $hook ]['callback'] )
1090              ) {
1091                  continue;
1092              }
1093  
1094              $results[] = spawn_cron( $gmt_time );
1095              break 2;
1096          }
1097      }
1098  
1099      if ( in_array( false, $results, true ) ) {
1100          return false;
1101      }
1102  
1103      return count( $results );
1104  }
1105  
1106  /**
1107   * Retrieves supported event recurrence schedules.
1108   *
1109   * The default supported recurrences are 'hourly', 'twicedaily', 'daily', and 'weekly'.
1110   * A plugin may add more by hooking into the {@see 'cron_schedules'} filter.
1111   * The filter accepts an array of arrays. The outer array has a key that is the name
1112   * of the schedule, for example 'monthly'. The value is an array with two keys,
1113   * one is 'interval' and the other is 'display'.
1114   *
1115   * The 'interval' is a number in seconds of when the cron job should run.
1116   * So for 'hourly' the time is `HOUR_IN_SECONDS` (`60 * 60` or `3600`). For 'monthly',
1117   * the value would be `MONTH_IN_SECONDS` (`30 * 24 * 60 * 60` or `2592000`).
1118   *
1119   * The 'display' is the description. For the 'monthly' key, the 'display'
1120   * would be `__( 'Once Monthly' )`.
1121   *
1122   * For your plugin, you will be passed an array. You can add your
1123   * schedule by doing the following:
1124   *
1125   *     // Filter parameter variable name is 'array'.
1126   *     $array['monthly'] = array(
1127   *         'interval' => MONTH_IN_SECONDS,
1128   *         'display'  => __( 'Once Monthly' )
1129   *     );
1130   *
1131   * @since 2.1.0
1132   * @since 5.4.0 The 'weekly' schedule was added.
1133   *
1134   * @return array {
1135   *     The array of cron schedules keyed by the schedule name.
1136   *
1137   *     @type array ...$0 {
1138   *         Cron schedule information.
1139   *
1140   *         @type int    $interval The schedule interval in seconds.
1141   *         @type string $display  The schedule display name.
1142   *     }
1143   * }
1144   */
1145  function wp_get_schedules() {
1146      $schedules = array(
1147          'hourly'     => array(
1148              'interval' => HOUR_IN_SECONDS,
1149              'display'  => __( 'Once Hourly' ),
1150          ),
1151          'twicedaily' => array(
1152              'interval' => 12 * HOUR_IN_SECONDS,
1153              'display'  => __( 'Twice Daily' ),
1154          ),
1155          'daily'      => array(
1156              'interval' => DAY_IN_SECONDS,
1157              'display'  => __( 'Once Daily' ),
1158          ),
1159          'weekly'     => array(
1160              'interval' => WEEK_IN_SECONDS,
1161              'display'  => __( 'Once Weekly' ),
1162          ),
1163      );
1164  
1165      /**
1166       * Filters the non-default cron schedules.
1167       *
1168       * @since 2.1.0
1169       *
1170       * @param array $new_schedules {
1171       *     An array of non-default cron schedules keyed by the schedule name. Default empty array.
1172       *
1173       *     @type array ...$0 {
1174       *         Cron schedule information.
1175       *
1176       *         @type int    $interval The schedule interval in seconds.
1177       *         @type string $display  The schedule display name.
1178       *     }
1179       * }
1180       */
1181      return array_merge( apply_filters( 'cron_schedules', array() ), $schedules );
1182  }
1183  
1184  /**
1185   * Retrieves the name of the recurrence schedule for an event.
1186   *
1187   * @see wp_get_schedules() for available schedules.
1188   *
1189   * @since 2.1.0
1190   * @since 5.1.0 {@see 'get_schedule'} filter added.
1191   *
1192   * @param string $hook Action hook to identify the event.
1193   * @param array  $args Optional. Arguments passed to the event's callback function.
1194   *                     Default empty array.
1195   * @return string|false Schedule name on success, false if no schedule.
1196   */
1197  function wp_get_schedule( $hook, $args = array() ) {
1198      $schedule = false;
1199      $event    = wp_get_scheduled_event( $hook, $args );
1200  
1201      if ( $event ) {
1202          $schedule = $event->schedule;
1203      }
1204  
1205      /**
1206       * Filters the schedule name for a hook.
1207       *
1208       * @since 5.1.0
1209       *
1210       * @param string|false $schedule Schedule for the hook. False if not found.
1211       * @param string       $hook     Action hook to execute when cron is run.
1212       * @param array        $args     Arguments to pass to the hook's callback function.
1213       */
1214      return apply_filters( 'get_schedule', $schedule, $hook, $args );
1215  }
1216  
1217  /**
1218   * Retrieves cron jobs ready to be run.
1219   *
1220   * Returns the results of _get_cron_array() limited to events ready to be run,
1221   * ie, with a timestamp in the past.
1222   *
1223   * @since 5.1.0
1224   *
1225   * @return array[] Array of cron job arrays ready to be run.
1226   */
1227  function wp_get_ready_cron_jobs() {
1228      /**
1229       * Filter to override retrieving ready cron jobs.
1230       *
1231       * Returning an array will short-circuit the normal retrieval of ready
1232       * cron jobs, causing the function to return the filtered value instead.
1233       *
1234       * @since 5.1.0
1235       *
1236       * @param null|array[] $pre Array of ready cron tasks to return instead. Default null
1237       *                          to continue using results from _get_cron_array().
1238       */
1239      $pre = apply_filters( 'pre_get_ready_cron_jobs', null );
1240  
1241      if ( null !== $pre ) {
1242          return $pre;
1243      }
1244  
1245      $crons    = _get_cron_array();
1246      $gmt_time = microtime( true );
1247      $results  = array();
1248  
1249      foreach ( $crons as $timestamp => $cronhooks ) {
1250          if ( $timestamp > $gmt_time ) {
1251              break;
1252          }
1253  
1254          $results[ $timestamp ] = $cronhooks;
1255      }
1256  
1257      return $results;
1258  }
1259  
1260  //
1261  // Private functions.
1262  //
1263  
1264  /**
1265   * Retrieves cron info array option.
1266   *
1267   * @since 2.1.0
1268   * @since 6.1.0 Return type modified to consistently return an array.
1269   * @access private
1270   *
1271   * @return array<int, array<string, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>>>|array{} Array of cron events.
1272   */
1273  function _get_cron_array() {
1274      $cron = get_option( 'cron' );
1275      if ( ! is_array( $cron ) ) {
1276          return array();
1277      }
1278  
1279      /**
1280       * @var array{ version: int, ...<int, array<string, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>>> }
1281       *     |array<int, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>> $cron
1282       */
1283      if ( ! isset( $cron['version'] ) ) {
1284          $cron = _upgrade_cron_array( $cron );
1285      }
1286  
1287      unset( $cron['version'] );
1288  
1289      /** @var array<int, array<string, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>>> $cron */
1290      return $cron;
1291  }
1292  
1293  /**
1294   * Updates the cron option with the new cron array.
1295   *
1296   * @since 2.1.0
1297   * @since 5.1.0 Return value modified to outcome of update_option().
1298   * @since 5.7.0 The `$wp_error` parameter was added.
1299   *
1300   * @access private
1301   *
1302   * @param array[] $cron     Array of cron info arrays from _get_cron_array().
1303   * @param bool    $wp_error Optional. Whether to return a WP_Error on failure. Default false.
1304   * @return bool|WP_Error True if cron array updated. False or WP_Error on failure.
1305   *
1306   * @phpstan-param array<int, array<string, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>>> $cron
1307   * @phpstan-return ( $wp_error is true ? true|WP_Error : bool )
1308   */
1309  function _set_cron_array( $cron, $wp_error = false ) {
1310      if ( ! is_array( $cron ) ) {
1311          $cron = array();
1312      }
1313  
1314      $cron['version'] = 2;
1315  
1316      $result = update_option( 'cron', $cron, true );
1317  
1318      if ( $wp_error && ! $result ) {
1319          return new WP_Error(
1320              'could_not_set',
1321              __( 'The cron event list could not be saved.' )
1322          );
1323      }
1324  
1325      return $result;
1326  }
1327  
1328  /**
1329   * Upgrades a cron info array.
1330   *
1331   * This function upgrades the cron info array to version 2.
1332   *
1333   * @since 2.1.0
1334   * @access private
1335   *
1336   * @param array $cron Cron info array from _get_cron_array().
1337   * @return array An upgraded cron info array.
1338   *
1339   * @phpstan-param array{ version: int, ...<int, array<string, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>>> }
1340   *               |array<int, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>> $cron
1341   * @phpstan-return array{ version: 2, ...<int, array<string, array<string, array{ schedule: string|false, args: array<mixed>, interval?: non-negative-int }>>> }
1342   */
1343  function _upgrade_cron_array( $cron ) {
1344      if ( isset( $cron['version'] ) && 2 === $cron['version'] ) {
1345          return $cron;
1346      }
1347  
1348      $new_cron = array();
1349  
1350      foreach ( (array) $cron as $timestamp => $hooks ) {
1351          foreach ( (array) $hooks as $hook => $args ) {
1352              $key = md5( serialize( $args['args'] ?? array() ) );
1353  
1354              $new_cron[ $timestamp ][ $hook ][ $key ] = $args;
1355          }
1356      }
1357  
1358      $new_cron['version'] = 2;
1359  
1360      update_option( 'cron', $new_cron, true );
1361  
1362      return $new_cron;
1363  }


Generated : Wed Nov 11 08:20:02 2026 Cross-referenced by PHPXref