Seditio Source
Root |
./othercms/b2evolution_7.2.3/cron/cron_exec.php
<?php
/**
 * Execute cron jobs.
 *
 * Example to use CLI:
 * >c:\php4\php cron_exec.php
 * >c:\php4\php-cli cron_exec.php
 */


/**
 * Include config
 */
require_once dirname(__FILE__).'/../conf/_config.php';

/**
 * Include main initialization
 * Note: This will initialize only the required objects ( e.g. $Session or $Hit objects will NOT be initialized in cli mode ).
 */
require_once $inc_path .'_main.inc.php';

// Start timer for cron job:
// (time is printed out after each action, @see cron_log_action_end())
$Timer->start( 'cron_exec' );

if(
$Settings->get( 'system_lock' ) )
{
// System is locked down for maintenance, Stop cron execution
   
echo 'The site is locked for maintenance. All scheduled jobs are postponed. No job was executed.';
    exit(
0);
}

/**
 * Cron support functions
 */
load_funcs( 'cron/_cron.funcs.php' );

// Register shutdown function to catch fatal errors:
register_shutdown_function( 'cron_job_shutdown' );
// Mark this script as cron job executing in order to catch function debug_die() here and store error log in cron log:
$is_cron_job_executing = true;

/**
 * @global integer Quietness.
 *         1 suppresses trivial/informative messages,
 *         2 suppresses success messages,
 *         3 suppresses errors.
 */
$quiet = 0;
if(
$is_cli )
{
// called through Command Line Interface, handle args:

    // Load required functions ( we need to load here, because in CLI mode it is not loaded )
   
load_funcs( '_core/_url.funcs.php' );

    if( isset(
$_SERVER['argc'], $_SERVER['argv'] ) )
    {
       
$argc = $_SERVER['argc'];
       
$argv = $_SERVER['argv'];
    }

    if( isset(
$argv) )
    {
// may not be set for CGI
       
foreach( array_slice($argv, 1) as $v )
        {
            switch(
$v )
            {
                case
'-h':
                case
'--help':
                   
// display help:
                   
echo $argv[0]." - Execute cron jobs for b2evolution\n";
                    echo
"\n";
                    echo
"Options:\n";
                    echo
" -q --quiet: Be quiet (do not output a message, if there are no jobs).\n";
                    echo
"             This is especially useful, when running as a cron job.\n";
                    echo
"             You can use this up to three times to increase quietness.\n";
                    echo
"             Successful runs can be made silent with \"-q -q\".\n";
                    exit(
0);
                    break;

                case
'-q':
                case
'--quiet':
                   
// increase quietness:
                   
$quiet++;
                    break;

                default:
                    echo
'Invalid option "'.$v.'". Use "-h" or "--help" for a list of options.'."\n";
                    die(
1);
            }
        }
    }

    global
$default_locale, $current_charset;

   
// We don't load _init_session.inc.php in CLI mode, so we set locale and DB connection charset here
   
locale_overwritefromDB();
   
locale_activate( $default_locale );

   
// Init charset handling - this will also set the encoding for MySQL connection
   
init_charsets( $current_charset );
}
elseif( !
is_admin_page() )
{
// This is a web request: (for testing purposes only. Not designed for production)

    // Make sure the response is never cached:
   
header_nocache();
   
header_content_type();

   
// Add CSS:
   
require_css( 'basic_styles.css', 'rsc_url' ); // the REAL basic styles
   
require_css( 'basic.css', 'rsc_url' ); // Basic styles
   
?>
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <title>Cron exec</title>
        <?php include_headlines() /* Add javascript and css files included by plugins and skin */ ?>
   </head>
    <body>
        <h1>Cron exec</h1>
        <p>This script will execute the next task in the cron queue.
        You should normally call it with the CLI (command line interface) version of PHP
        and automate that call through a cron.</p>
    <?php
}

/*
 * The following will feel a little bloated...
 * BUT it is actually a pretty nifty design to prevent double execution of tasks without any transaction!
 * The trick is to rely on the primary key of the cron__log table.
 */

// Get next task to run in queue which has not started execution yet:
$sql = 'SELECT *
                    FROM T_cron__task LEFT JOIN T_cron__log ON ctsk_ID = clog_ctsk_ID
                 WHERE clog_ctsk_ID IS NULL
                     AND ctsk_start_datetime <= '
.$DB->quote( date2mysql($localtimenow) ).'
                 ORDER BY ctsk_start_datetime ASC, ctsk_ID ASC
                 LIMIT 1'
;
$task = $DB->get_row( $sql, OBJECT, 0, 'Get next task to run in queue which has not started execution yet' );
$error_task = NULL;

if( empty(
$task ) )
{
   
cron_log( 'There is no task to execute yet.', 0 );
}
else
{
   
$ctsk_ID = $task->ctsk_ID;
   
$ctsk_name = cron_job_name( $task->ctsk_key, $task->ctsk_name, $task->ctsk_params );

   
// Initialize a var to count a number of cron job actions:
   
$cron_log_actions_num = NULL;

   
// Store key of currently executing cron job:
   
$executing_cron_task_key = $task->ctsk_key;

   
cron_log( 'Requesting lock on task #'.$ctsk_ID.' ['.$ctsk_name.']', 0 );

   
$DB->halt_on_error = false;
   
$DB->show_errors = false;
   
$cron_timestart = $localtimenow;
   
$sql = 'INSERT INTO T_cron__log( clog_ctsk_ID, clog_realstart_datetime, clog_status)
                    VALUES( '
.$ctsk_ID.', '.$DB->quote( date2mysql( $cron_timestart ) ).', "started" )';
   
// Duplicate query for tests!
    // $DB->query( $sql, 'Request lock' );
   
if( $DB->query( $sql, 'Request lock' ) != 1 )
    {
// This has no affected exactly ONE row: error! (probably locked -- duplicate key -- by a concurrent process)
       
$DB->show_errors = true;
       
$DB->halt_on_error = true;
       
cron_log( 'Could not lock. Task is probably handled by another process.', 2 );
    }
    else
    {
        if( !empty(
$task->ctsk_repeat_after ) )
        {
// This task wants to be repeated:
            // Note: we use the current time for 2 reasons: 1) prevent scheduling something in the past AND 2) introduce variety so that everyone doesn't run his repeated tasks at the same exact time, especially pings, pollings...
           
if( $task->ctsk_repeat_after < 86400 )
            {    
// If "Repeat every" < 1 day, then start from current timestamp:
               
$new_start_datetime = $localtimenow;
            }
            else
            {    
// If "Repeat every" >= 1 day, then start from TODAY at time of "Schedule date":
               
$new_start_datetime = strtotime( date( 'Y-m-d', $localtimenow ).' '.substr( $task->ctsk_start_datetime, 11 ) );
            }
           
$new_start_datetime += $task->ctsk_repeat_after;
            if(
$task->ctsk_key == 'poll-antispam-blacklist' )
            {
// THIS IS A HACK. Guess why we need that!? :P  Please do not override or you'll kill our server :(
               
$new_start_datetime += rand( 43200, 86400 ); // 12 to 24 hours
           
}
            else
            {
// Normal
               
if( ! empty( $task->ctsk_repeat_variation ) )
                {
// Include variation param as random +/- time value
                   
$new_start_datetime += rand( 0, 2 * $task->ctsk_repeat_variation ) - $task->ctsk_repeat_variation;
                }
            }
           
$ctsk_name_insert = empty( $task->ctsk_name ) ? 'NULL' : $DB->quote( $task->ctsk_name );
           
$sql = 'INSERT INTO T_cron__task( ctsk_start_datetime, ctsk_repeat_after, ctsk_repeat_variation, ctsk_name, ctsk_key, ctsk_params )
                            VALUES( '
.$DB->quote( date2mysql( $new_start_datetime ) ).', '.$DB->quote( $task->ctsk_repeat_after ).', '.$DB->quote( $task->ctsk_repeat_variation ).', '
                                               
.$ctsk_name_insert.', '.$DB->quote( $task->ctsk_key ).', '.$DB->quote( $task->ctsk_params ).' )';
           
$DB->query( $sql, 'Schedule repeated task.' );
        }

       
$DB->show_errors = true;
       
$DB->halt_on_error = true;
       
cron_log( 'Starting task #'.$ctsk_ID.' ['.$ctsk_name.'] at '.date( 'H:i:s', $localtimenow ).'.', 1 );

        if( empty(
$task->ctsk_params) )
        {
           
$cron_params = array();
        }
        else
        {
           
$cron_params = unserialize( $task->ctsk_params );
        }

       
// The job may need to know its ID and name (to set logical locks for example):
       
$cron_params['ctsk_ID'] = $ctsk_ID;

       
// Set max execution time for each cron job separately:
       
set_max_execution_time( $Settings->get( 'cjob_timeout_'.$task->ctsk_key ) );

       
// Try to execute cron job:
       
set_error_handler( 'cron_job_error_handler' );
        try
        {    
// EXECUTE CRON JOB:
           
$error_message = call_job( $task->ctsk_key, $cron_params );
        }
        catch(
Exception $ex )
        {    
// Unexpected error:
           
$result_status = 'error';
           
$error_message = "\n".'b2evolution caught an UNEXPECTED ERROR: '
               
.'<b>File:</b> '.$ex->getFile().', '
               
.'<b>Line:</b> '.$ex->getLine().', '
               
.'<b>Message:</b> '.$ex->getMessage();
           
$result_message .= $error_message;
            echo
nl2br( $result_message );
           
// We must rollback any started transaction in order to proper update cron job log below:
           
$DB->rollback();
        }
       
restore_error_handler();

        if( ! empty(
$error_message ) )
        {    
// Set error task in order to report by email to admin in the function detect_timeout_cron_jobs():
           
$error_task = array(
                   
'ID'      => $ctsk_ID,
                   
'name'    => $ctsk_name,
                   
'message' => $error_message,
                );

            if(
$result_status == 'imap_error' &&
                (
$max_consecutive_imap_errors = $Settings->get( 'cjob_imap_error_'.$task->ctsk_key ) ) > 1 )
            {    
// Check if imap error task can be reported by email to admin:
               
$previous_tasks_SQL = new SQL( 'Check consecutive imap error cron jobs' );
               
$previous_tasks_SQL->SELECT( 'clog_status' );
               
$previous_tasks_SQL->FROM( 'T_cron__log' );
               
$previous_tasks_SQL->FROM_add( 'INNER JOIN T_cron__task ON clog_ctsk_ID = ctsk_ID' );
               
$previous_tasks_SQL->WHERE( 'ctsk_key = '.$DB->quote( $task->ctsk_key ) );
               
$previous_tasks_SQL->ORDER_BY( 'clog_realstart_datetime DESC' );
               
// Skip first task because this is a currently executing task still has a status "started" in DB,
                // but after update below the status will be "imap_error":
               
$previous_tasks_SQL->LIMIT( '1, '.( $max_consecutive_imap_errors - 1 ) );
               
$previous_tasks = $DB->get_col( $previous_tasks_SQL );
               
$previous_tasks[] = 'imap_error'; // append status of the currently executing task
               
if( count( $previous_tasks ) < $max_consecutive_imap_errors ||
                   
count( array_unique( $previous_tasks ) ) > 1 )
                {    
// If X previous consecutive tasks have no same status "IMAP error",
                    // unset error task in order to don't report by email to admin:
                   
$error_task = NULL;
                }
            }
        }

       
// Record task as finished:
       
if( empty( $timestop ) )
        {
           
$timestop = time() + $time_difference;
        }

        if(
is_array( $result_message ) )
        {    
// If result is array we should store it as serialized data
           
$result_message = serialize( $result_message );
        }

       
$sql = ' UPDATE T_cron__log
                                SET clog_status = '
.$DB->quote( $result_status ).',
                                        clog_realstop_datetime = '
.$DB->quote( date2mysql( $timestop ) ).',
                                        clog_messages = '
.$DB->quote( $result_message ) /* May be NULL */.',
                                        clog_actions_num = '
.$DB->quote( $cron_log_actions_num ).'
                            WHERE clog_ctsk_ID = '
.$ctsk_ID;
       
$DB->query( $sql, 'Record task as finished.' );
    }

   
// Unset data of the executed cron job:
   
unset( $ctsk_ID );
    unset(
$executing_cron_task_key );
}


//echo 'detecting timeouts...';
// Detect timed out tasks:
detect_timeout_cron_jobs( $error_task );



if( !
$is_cli && ! is_admin_page() )
{    
// This is a web request:
   
echo '<p><a href="cron_exec.php">Refresh Now!</a></p>';
    echo
'<p>This page should refresh automatically in 15 seconds...</p>';
    echo
'<!-- This is invalid HTML but it is SOOOOOO helpful! (Delay will be triggered when we reach that point -->';
    echo
'<meta http-equiv="Refresh" content="15" />';

   
// Add JavaScript and CSS files included by plugins and skin
   
include_footerlines();
   
?>
   </body>
    </html>
    <?php
}

// Stop timer of cron job:
$Timer->stop( 'cron_exec' );
?>