[ Index ]

PHP Cross Reference of WordPress

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


Generated: Sun Apr 18 01:00:12 2021 Cross-referenced by PHPXref 0.7.1