I'm writing a plugin that uses WordPress Cron.

Here the code I am using to add and remove the cron event. On plugin activation it creates the cron event, and on plugin de-activation it removes the event:

//On plugin activation schedule the cron event
register_activation_hook( __FILE__, 'bg_create_pods_midnight_cron' );
function bg_create_pods_midnight_cron(){
  //Use wp_next_scheduled to check if the event is already scheduled
  $timestamp = wp_next_scheduled( 'bg_pods_midnight_cron' );
  //If $timestamp == false schedule event since it hasn't been done previously
  if( $timestamp == false ){
    //Schedule the event for today at midnight, then to repeat daily using the hook 'bg_pods_midnight_cron'
    wp_schedule_event( strtotime('today midnight'), 'daily', 'bg_pods_midnight_cron' );

//On plugin de-activation de-schedule the cron event
register_deactivation_hook( __FILE__, 'bg_remove_pods_midnight_cron' );
function bg_remove_pods_midnight_cron(){
   wp_clear_scheduled_hook( 'bg_pods_midnight_cron' );

I then created a function that hooks into the cron event I just created. The function is supposed to update some custom fields.

It works fine when hooked into a post save hook, but it doesn't do anything (custom fields are not updated) when it's hooked into my bg_pods_midnight_cron which I created above.

add_action( 'bg_pods_midnight_cron', 'bg_update_country_speed_averages');
function bg_update_country_speed_averages() {

   $vpn_args = array(
      'posts_per_page'  => -1,
      'post_type'       => 'vpn',
   $vpns = get_posts( $vpn_args );

   foreach ($vpns as $vpn) {

      $vpn_id = $vpn->ID;

      // Country IDs and the starting string of their matching "avg" meta key on VPN posts
      $country_keys = array(
         292   => 'avg_australia',         
         529   => 'avg_united_states',
         357   => 'avg_france',
         443   => 'avg_new_zealand',
         326   => 'avg_china',
         517   => 'avg_turkey',
         393   => 'avg_japan',
         528   => 'avg_united_kingdom',
         439   => 'avg_netherlands',
         319   => 'avg_canada',

      foreach ($country_keys as $country_id => $meta_key_start) {

         $speed_args = array(
            'posts_per_page'   => 30,
            'post_type'        => 'speed_test',
            'meta_query' => array(
                     'key' => 'vpn',
                     'value' => $vpn_id,
                     'key' => 'server_country',
                     'value' => $country_id,
         $speed_tests = get_posts( $speed_args );

         $download_meta_key = $meta_key_start . "_download_speed_30_days_input";
         $upload_meta_key = $meta_key_start . "_upload_speed_30_days_input";
         $latency_meta_key = $meta_key_start . "_latency_30_days_input";

         if (!empty($speed_tests)) {
            // Get all the comparions
            $download_speed_array = array();
            $upload_speed_array = array();
            $latency_array = array();
            foreach ($speed_tests as $speed_test) {
               if (!empty(get_post_meta( $speed_test->ID, 'download_speed_comparison', true ))) {
                  $download_speed_array[] = get_post_meta( $speed_test->ID, 'download_speed_comparison', true );
               if (!empty(get_post_meta( $speed_test->ID, 'upload_speed_comparison', true ))) {
                  $upload_speed_array[] = get_post_meta( $speed_test->ID, 'upload_speed_comparison', true );
               if (!empty(get_post_meta( $speed_test->ID, 'latency_comparison', true ))) {
                  $latency_array[] = get_post_meta( $speed_test->ID, 'latency_comparison', true );

            // Calculate an average
            if ( !empty($download_speed_array) ) {
               $download_speed_avg_last_30_days = array_sum($download_speed_array) / count($download_speed_array);
            if ( !empty($upload_speed_array) ) {
               $upload_speed_avg_last_30_days = array_sum($upload_speed_array) / count($upload_speed_array);
            if ( !empty($latency_array) ) {
               $latency_avg_last_30_days = array_sum($latency_array) / count($latency_array);

            // Update the vpn custom field with the country average speed
            if (metadata_exists( 'post', $vpn_id, $download_meta_key)) {
               update_post_meta( $vpn_id, $download_meta_key, $download_speed_avg_last_30_days);
            if (metadata_exists( 'post', $vpn_id, $upload_meta_key)) {
               update_post_meta( $vpn_id, $upload_meta_key, $upload_speed_avg_last_30_days);
            if (metadata_exists( 'post', $vpn_id, $latency_meta_key)) {
               update_post_meta( $vpn_id, $latency_meta_key, $latency_avg_last_30_days);
         } else {
            // Update the vpn custom field with 0
            if (metadata_exists( 'post', $vpn_id, $download_meta_key)) {
               update_post_meta( $vpn_id, $download_meta_key, 0);
            if (metadata_exists( 'post', $vpn_id, $upload_meta_key)) {
               update_post_meta( $vpn_id, $upload_meta_key, 0);
            if (metadata_exists( 'post', $vpn_id, $latency_meta_key)) {
               update_post_meta( $vpn_id, $latency_meta_key, 0);

I know that WordPress Cron only fires when someone visits your website, so that is why I installed WP Control to run the cron on demand for debugging. You can see a screenshot of WP Control below.

The cron hook is registered correctly. And my function is hooked into that correctly. But when I click the "run now" button the custom fields are not updating.

enter image description here

I can't figure out why it works when hooked into the post save, but does not work when hooked into my custom cron.

  • u sure this is not running properly? in ur code u wp_schedule_event ur event daily, means the function will only run once every 24 hours, no matter how often u trigger cron.. make sure to clear that schedule, when u change the value, or it will not be changed at all.. – honk31 Jun 12 '18 at 8:17
  • @honk31 I was under the impression that "run now" would trigger the cron and run the function immediately (i.e. ignore the 'daily' time and run now). Otherwise that buttons seems useless. Nonetheless, I tried changing it to hourly (and confirmed the new cron schedule inside WP Control) but the custom fields are still not being updated. – Ben Turner Jun 13 '18 at 2:05
  • not sure about wp control, never used it, but usually, when u set up a cron and say it should run daily it will only run every 24h. maybe i was wrong with that. but another apporoach: when u simply call bg_update_country_speed_averages() inside your functions.php and reload the page, does this work properly? – honk31 Jun 13 '18 at 7:49
  • @honk31 Ok I'm not sure what was happening before, but thanks to your advice I narrowed it down to metadata_exists( 'post', $vpn_id, $download_meta_key) which was causing the problems. I could just remove that check, but update_post_meta() will create the post meta if it doesn't already exist, which I do not want to happen. I'm guessing that maybe metadata_exists() doesn't play nice with custom posts. I think the problem might be coming from the first param. So I tired using post and also vpn (which is the name of my custom post type I'm updating) but both of those failed. – Ben Turner Jun 14 '18 at 8:34
  • hi @Ben. please update your question or create a new one, since this is getting confusing down here in the comments.. – honk31 Jun 14 '18 at 8:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.