Cron Status

Frequently Asked Questions

Integration: How to create a Cron Job?

The information below is for extension authors who want to make their Cron Tasks compatible with Cron Status and display normally on Cron Status page in the ACP.

The first step

If you already have a file for your Cron Job, you can skip this step.

At first you need to create your Cron Job file. Let's give it the name cron_task.php.

For example you are developing the extension vendor/ext.
The extension's folder is located in ext/vendor/ext path from the board root.

Let's put our Cron Job file in ext/vendor/ext/cron/task/cron_task.php.

How can we tell the board that our cron_task.php file contains a Cron Job?

We need to create a service in the config folder of our extension.

You can use an existing configuration file with the extension .yml or you can create a new one, e.g. cron.yml.

The following code should exist in your configuration file (note that the line services: is needed to be present in the file only once):

Code: Select all

services:
    vendor.ext.cron.task.cron_task:
        class: vendor\ext\cron\task\cron_task
        arguments:
            - @config
        calls:
            - [set_name, [cron.task.cron_task]]
        tags:
            - { name: cron.task }


OK. Then you need to edit your cron_task.php to create the code of your Cron Job.

You can study some examples of Cron Job files:
autogroups_check, rxu_tidy_warnings.

Core Cron Jobs are also the examples that you can study (e.g. tidy_sessions)

In the case of our example you need to add the following header to the file:

Code: Select all

<?php

namespace vendor\ext\cron\task;

class cron_task extends \phpbb\cron\task\base


Then you need to define the constructor and the necessary methods of your cron_task class.

The methods are described here.

Now you've completed the preparations and you can choose the names of configuration variables.

Simple solution: _gc endings

The _gc ending is used as the default ending for all Cron events (we suppose that config variables with _gc endings are Cron Tasks, other variables are not searched for (with some exceptions)).

The configuration variable with the _gc ending in its name is the interval between two Cron Jobs of the same Cron Job class. This is the time in seconds that means the delay before the next Cron Job can run.

The configuration variable with the _last_gc ending in its name is the last time when the Cron Job ran. The format of this time is the format that is used in the time() function of PHP.


Let's look at an example from the core (tidy_sessions):

Code: Select all

   /**
   * Returns whether this cron task should run now, because enough time
   * has passed since it was last run.
   *
   * The interval between session tidying is specified in board
   * configuration.
   *
   * @return bool
   */
   public function should_run()
   {
      return $this->config['session_last_gc'] < time() - $this->config['session_gc'];
   }


Note: the core Cron Jobs are handled differently than the Cron Jobs from extensions.
The core Cron Jobs are detected almost individually.

You need to specify the names of configuration variables like class_name_last_gc and class_name_gc where class_name is the name of the Cron Task's class.

In our example the names of configuration variables should be cron_task_last_gc and cron_task_gc.

Don't forget to add those variables to the database in one of your migration files.
Example:

Code: Select all

   public function update_data()
   {
      return array(
         array('config.add', array('cron_task_last_gc', 0)), // last run
         array('config.add', array('cron_task_gc', (60 * 60 * 24))), // seconds between run; 1 day
      );
   }


Combining the notes above together we can create a simple example cron_task.php file:

Code: Select all

<?php
/* Put your header comments here. */

namespace vendor\ext\cron\task;

class cron_task extends \phpbb\cron\task\base
{
   protected $config;

   /**
   * Constructor.
   *
   * @param \phpbb\config\config $config The config
   */
   public function __construct(\phpbb\config\config $config)
   {
      $this->config = $config;
   }

   /**
   * Runs this cron task.
   *
   * @return null
   */
   public function run()
   {
      // Your code goes here.
      // Do the actions that are the purpose for the Cron Job that we have created.

      // Do not forget to update the configuration variable for last run time.
      $this->config->set('cron_task_last_gc', time());
   }

   /**
   * Returns whether this cron task should run now, because enough time
   * has passed since it was last run.
   *
   * @return bool
   */
   public function should_run()
   {
      return $this->config['cron_task_last_gc'] < time() - $this->config['cron_task_gc'];
   }
}


You can also add other arguments after @config specified in this example in the same way as they are set for listeners.

Simple solution example

This is the full example of implementation of a Cron Job to your extension. The example contains the most preferred way to specify configuration variables for your Cron Task.

Please change vendor, ext and cron_task names in this example to your own names according to your extension.

Note: The names of your Cron Task's configuration variables should be unique.

The best way to specify them is to add the full name of your extension as a prefix: vendor_ext_my_cron_task_last_gc and vendor_ext_my_cron_task_gc.
In this case change the class name and the file name of your Cron Job to vendor_ext_my_cron_task.

Example migration file (migrations/my_migration.php).

Code: Select all

<?php
/* Put your header comments here. */

namespace vendor\ext\migrations;

class my_migration extends \phpbb\db\migration\migration
{
   public function effectively_installed()
   {
      return isset($this->config['cron_task_gc']);
   }

   static public function depends_on()
   {
      return array('\phpbb\db\migration\data\v310\dev');
   }

   public function update_data()
   {
      return array(
         array('config.add', array('cron_task_last_gc', 0)), // last run
         array('config.add', array('cron_task_gc', (60 * 60 * 24))), // seconds between run; 1 day
      );
   }
}


Example configuration file (config/services.yml).

Code: Select all

services:
    vendor.ext.cron.task.cron_task:
        class: vendor\ext\cron\task\cron_task
        arguments:
            - @config
        calls:
            - [set_name, [cron.task.cron_task]]
        tags:
            - { name: cron.task }


Example cron task file (cron/task/cron_task.php).

Code: Select all

<?php
/* Put your header comments here. */

namespace vendor\ext\cron\task;

class cron_task extends \phpbb\cron\task\base
{
   protected $config;

   /**
   * Constructor.
   *
   * @param \phpbb\config\config $config The config
   */
   public function __construct(\phpbb\config\config $config)
   {
      $this->config = $config;
   }

   /**
   * Runs this cron task.
   *
   * @return null
   */
   public function run()
   {
      // Your code goes here.
      // Do the actions that are the purpose for the Cron Job that we have created.

      // Do not forget to update the configuration variable for last run time.
      $this->config->set('cron_task_last_gc', time());
   }

   /**
   * Returns whether this cron task should run now, because enough time
   * has passed since it was last run.
   *
   * @return bool
   */
   public function should_run()
   {
      return $this->config['cron_task_last_gc'] < time() - $this->config['cron_task_gc'];
   }
}


Professional solution: your own endings

If you want your own Cron Jobs with no _gc endings to work with Cron Status, you can use the event "boardtools.cronstatus.modify_cron_config" to add any of your Cron Jobs to the rows array.

If you don't want to use _last_gc suffix for your new task dates, you will also need to use the event "boardtools.cronstatus.modify_cron_task" to manually handle your task.

All times should be in the time() function format. (The same as for other dates in phpBB.)

Simply work with the $event['rows'] in your listener.php file in the same way as with $lang in your language files.

Professional solution example

Please use this solution only when it's absolutely needed.
It's much easier to specify proper _gc endings to use automatic detection.


Example migration file (migrations/my_migration.php).

Code: Select all

<?php
/* Put your header comments here. */

namespace vendor\ext\migrations;

class my_migration extends \phpbb\db\migration\migration
{
   public function effectively_installed()
   {
      return isset($this->config['cron_task_custom_gc']);
   }

   static public function depends_on()
   {
      return array('\phpbb\db\migration\data\v310\dev');
   }

   public function update_data()
   {
      return array(
         array('config.add', array('cron_task_custom_last_gc', 0)), // last run
         array('config.add', array('cron_task_custom_gc', (60 * 60 * 24))), // seconds between run; 1 day
      );
   }
}


Example configuration file (config/services.yml).

Code: Select all

services:
    vendor.ext.cron.task.cron_task:
        class: vendor\ext\cron\task\cron_task
        arguments:
            - @config
        calls:
            - [set_name, [cron.task.cron_task]]
        tags:
            - { name: cron.task }

    vendor.ext.listener:
        class: vendor\ext\event\listener
        arguments:
            - @config
            - @service_container
        tags:
            - { name: event.listener }


Example cron task file (cron/task/cron_task.php).

Code: Select all

<?php
/* Put your header comments here. */

namespace vendor\ext\cron\task;

class cron_task extends \phpbb\cron\task\base
{
   protected $config;

   /**
   * Constructor.
   *
   * @param \phpbb\config\config $config The config
   */
   public function __construct(\phpbb\config\config $config)
   {
      $this->config = $config;
   }

   /**
   * Runs this cron task.
   *
   * @return null
   */
   public function run()
   {
      // Your code goes here.
      // Do the actions that are the purpose for the Cron Job that we have created.

      // Do not forget to update the configuration variable for last run time.
      $this->config->set('cron_task_custom_last_gc', time());
   }

   /**
   * Returns whether this cron task should run now, because enough time
   * has passed since it was last run.
   *
   * @return bool
   */
   public function should_run()
   {
      return $this->config['cron_task_custom_last_gc'] < time() - $this->config['cron_task_custom_gc'];
   }
}


Example event listener file (event/listener.php).

Code: Select all

<?php
/* Put your header comments here. */

namespace vendor\ext\event;

/**
* @ignore
*/
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* Event listener
*/
class listener implements EventSubscriberInterface
{
   protected $config;
   protected $phpbb_container;

   /**
   * Constructor
   *
   * @param \phpbb\config\config $config          Config object
   * @param object               $phpbb_container Container object
   */
   public function __construct(\phpbb\config\config $config, $phpbb_container)
   {
      $this->config = $config;
      $this->phpbb_container = $phpbb_container;
   }

   static public function getSubscribedEvents()
   {
      return array(
         'boardtools.cronstatus.modify_cron_task'   => 'add_my_cron_task',
         'boardtools.cronstatus.modify_cron_config'   => 'modify_cronlock',
      );
   }

   public function add_my_cron_task($event)
   {
      // Detect our task (defined by its name).
      if ($event['task_name'] === 'cron.task.cron_task_test')
      {
         // Get configuration variables.
         $last_task_date = $this->config['cron_task_custom_last_gc'];
         $task_interval = $this->config['cron_task_custom_gc'];
         
         // Set last task date.
         $event['task_date'] = $last_task_date;

         // Set next task date.
         $event['new_task_date'] = $last_task_date + $task_interval;
      }
   }

   public function modify_cronlock($event)
   {
      // Get configuration variable.
      $last_task_date = $this->config['cron_task_custom_last_gc'];

      // 'last_task_date' variable is only specified in Cron Status 3.1.2 and higher.
      if (isset($event['last_task_date']))
      {
         if ($last_task_date >= $event['last_task_date'])
         {
            $event['cronlock'] = 'cron_task'; // The name of our Cron Job.
            $event['last_task_date'] = $last_task_date; // Update the time.
         }
      }
      // Workaround for Cron Status 3.1.1.
      // The maximum value for the date of the cronlock is not passed to the event object.
      // We need to find it again.
      else if ($last_task_date >= $this->phpbb_container->get('boardtools.cronstatus.listener')->maxValueInArray($event['rows'], 'config_value'))
      {
         $event['cronlock'] = 'cron_task'; // The name of our Cron Job.
         // Update the time.
         $rows = $event['rows'];
         $rows[] = array(
            "config_name"   => "cron_task_last_gc", // Any name ending with '_last_gc'.
            "config_value"   => $last_task_date
         );
         $event['rows'] = $rows;
      }
   }
}


Have a nice extension! ;)