1
#1
Remove cron.php image and use a real CRON task!
Posted May 18th 2014, 1:26pm
This topic is more technical in nature than most topics in this forum. If you are unfamiliar with CRON jobs, I suggest familiarizing yourself with their concept and use before attempting this modification. IMPORTANT: Please make certain your hosting company supports CRON jobs, AND supports running the CLI (command line interface) version of PHP!


phpBB3 is an old program...we all know that. When it was written in 2005/2006, most hosting companies did not allow CRON jobs to be run unless you had a dedicated server. phpBB used a novel method to handle database cleanup and email tasks -- it created a PHP-based 1x1 transparent GIF image that did all sorts of CRON-type tasks. The problem is that such a method uses far more resources that a true CRON job, and on even a moderately-busy board, this can create problems. It's now 2014, and today, even some free hosting companies support CRON jobs.

You might be wondering...what's this CRON thingie? A CRON job is a program run by the server in the background at regular intervals. It's oftentimes used for data backups, and in the case of phpBB, it can be used to send email as well as to clean up garbage in heavily-accessed database tables. Since it is run in the background, it will not interfere with any actions on your site.

It turns out that converting the phpBB cron.php "image" into an actual CRON task is fairly simple. First, save the following to a file named dd_cron.php, and place the file in the phpBB root directory (where config.php and viewtopic.php are located).

<?php
/*
CRON script for phpBB garbage management
Copyright © 2014 by Dion Designs.
All Rights Reserved.

This is designed to run as a system CRON job

Run every 60 minutes for a small board (under 15 registered users every 15 minutes)
Run every 30 minutes for a medium board (25-35 registered users every 15 minutes)
Run every 15 minutes for a medium-large board (50-75 registered users every 15 minutes)
Run every 5 minutes for a very large board (over 150 registered users every 15 minutes)
*/
define('IN_PHPBB', true);
define('IN_CRON', true);
$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : './';
$phpEx = substr(strrchr(__FILE__, '.'), 1);
include($phpbb_root_path . 'common.' . $phpEx);

/*------------------------------------------
tidy warnings (once every day+30min)
------------------------------------------*/
if ($config['warnings_expire_days'] && $config['warnings_last_gc'] < (time() - 88200)) {
set_config('warnings_last_gc', time(), true);

$expire_date = time() - ($config['warnings_expire_days'] * 86400);
$warning_list = $user_list = array();

$sql = 'SELECT * FROM ' . WARNINGS_TABLE . " WHERE warning_time < $expire_date";
$result = $db->sql_query($sql);

while ($row = $db->sql_fetchrow($result)) {
$warning_list[] = $row['warning_id'];
$user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1;
}
$db->sql_freeresult($result);

if (sizeof($warning_list)) {
$db->sql_transaction('begin');

$sql = 'DELETE FROM ' . WARNINGS_TABLE . ' WHERE ' . $db->sql_in_set('warning_id', $warning_list);
$db->sql_query($sql);

foreach ($user_list as $user_id => $value) {
$sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value WHERE user_id = $user_id";
$db->sql_query($sql);
}

$db->sql_transaction('commit');
}
}

/*------------------------------------------
tidy logins/CAPTCHA (once every day+15min)
------------------------------------------*/
if ($config['session_last_gc'] < (time() - 87300)) {
set_config('session_last_gc', time(), true);

$max_autologin_time = ($config['max_autologin_time']) ? (int) $config['max_autologin_time'] : 365;

$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' WHERE last_login < ' . (time() - (86400 * $max_autologin_time));
$db->sql_query($sql);

$sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . ' WHERE attempt_time < ' . (time() - (int) $config['ip_login_limit_time']);
$db->sql_query($sql);

$name = basename($config['captcha_plugin']);
if (!class_exists($name)) {
include($phpbb_root_path . "includes/captcha/plugins/{$name}_plugin." . $phpEx);
}
call_user_func(array($name, 'garbage_collect'), 0);
}

/*------------------------------------------
tidy database (once every 4.25 hours)
------------------------------------------*/
if ($config['database_last_gc'] < (time() - 15300)) {
set_config('database_last_gc', time(), true);

$sql = 'SELECT forum_id FROM ' . FORUMS_TABLE;
$result = $db->sql_query($sql);

$forum_ids = array(0);
while ($row = $db->sql_fetchrow($result)) {
$forum_ids[] = $row['forum_id'];
}
$db->sql_freeresult($result);

$sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
$db->sql_query($sql);

$sql = 'DELETE FROM ' . ACL_USERS_TABLE . ' WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
$db->sql_query($sql);
}

/*------------------------------------------
tidy search (once every hour)
------------------------------------------*/
$search_type = basename($config['search_type']);
if ($config['search_last_gc'] < (time() - 3600) && file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx)) {
include_once("{$phpbb_root_path}includes/search/$search_type.$phpEx");
$error = false;
$search = new $search_type($error);
if (!$error) {
$search->tidy();
}
}

/*------------------------------------------
tidy cache (always)
------------------------------------------*/
if (method_exists($cache, 'tidy')) {
$cache->tidy();
}

/*------------------------------------------
tidy expired sessions (always)
------------------------------------------*/
$sql = 'SELECT session_user_id, session_page, MAX(session_time) AS recent_time FROM ' . SESSIONS_TABLE . '
WHERE session_time < ' . (time() - (int) $config['session_length']) . ' AND session_autologin = 1
GROUP BY session_user_id, session_page';
$result = $db->sql_query($sql);

while ($row = $db->sql_fetchrow($result)) {
$sql = 'UPDATE ' . USERS_TABLE . ' SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
WHERE user_id = " . (int) $row['session_user_id'];
$db->sql_query($sql);
}

$db->sql_freeresult($result);

$sql = 'DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_time < ' . (time() - (int) $config['session_length']);
$db->sql_query($sql);

/*------------------------------------------
tidy email (always, must run last)
------------------------------------------*/
if (file_exists($phpbb_root_path . 'cache/queue.' . $phpEx)) {
include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
$queue = new queue();
$queue->process();
}

// and we're done!
garbage_collection();
exit;
?>

You will now create the CRON job. Please note the suggestions in the dd_cron.php file for the time interval for the CRON job, and decide on the interval you want to use. In your hosting panel, there should be an option to set CRON jobs. Select that option, and select the time interval. Next, after replacing /path/to/phpbb with the actual server path to your phpbb root directory, enter the following line as the actual CRON task to run:

php -q /path/to/phpbb/dd_cron.php >/dev/null 2>&1

Save the task according to the instructions in your hosting panel. Finally, locate the following code in the page_footer() function in includes/functions.php:

	// Call cron-type script
$call_cron = false;
if (!defined('IN_CRON') && $run_cron && !$config['board_disable'] && !$user->data['is_bot'])
{
$call_cron = true;
$time_now = (!empty($user->time_now) && is_int($user->time_now)) ? $user->time_now : time();

// Any old lock present?
if (!empty($config['cron_lock']))
{
$cron_time = explode(' ', $config['cron_lock']);

// If 1 hour lock is present we do not call cron.php
if ($cron_time[0] + 3600 >= $time_now)
{
$call_cron = false;
}
}
}

// Call cron job?
if ($call_cron)
{
$cron_type = '';

if ($time_now - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
{
// Process email queue
$cron_type = 'queue';
}
else if (method_exists($cache, 'tidy') && $time_now - $config['cache_gc'] > $config['cache_last_gc'])
{
// Tidy the cache
$cron_type = 'tidy_cache';
}
else if ($config['warnings_expire_days'] && ($time_now - $config['warnings_gc'] > $config['warnings_last_gc']))
{
$cron_type = 'tidy_warnings';
}
else if ($time_now - $config['database_gc'] > $config['database_last_gc'])
{
// Tidy the database
$cron_type = 'tidy_database';
}
else if ($time_now - $config['search_gc'] > $config['search_last_gc'])
{
// Tidy the search
$cron_type = 'tidy_search';
}
else if ($time_now - $config['session_gc'] > $config['session_last_gc'])
{
$cron_type = 'tidy_sessions';
}

if ($cron_type)
{
$template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=' . $cron_type) . '" width="1" height="1" alt="cron" />');
}
}

Add /* to the start, and */ to the end of this block of code -- that will comment out the code. Save the file. If you ever need to restore the code, remove the /* and */.

And that's it! You now have a real CRON task that will run in the background as opposed to running when a user displays a page.

Sadly, the original cron.php file is still needed if you have any forums set to auto-prune. The viewforum.php file loads the cron.php "image" to handle the pruning, and since the task is forum-based, it cannot be run as a traditional CRON job.

Dion Designs to the rescue. :) The next post will show you how to automate the forum pruning task so you can get rid of the cron.php "image" forever!
φ
Posts: 1599
Joined: March 12th 2009, 11:00pm
Location: Uncertain due to momentum
Likes Given: 26
Likes Received: 357
1
#2
Remove cron.php image and use a real CRON task!
Posted May 18th 2014, 2:10pm
Now that you have your CRON job set up, I'm sure you want to get rid of the final CRON-type task that requires the cron.php "image". Here's how to do that. Open the viewforum.php file and locate the following:

// Do the forum Prune thang - cron type job ...
if ($forum_data['prune_next'] < time() && $forum_data['enable_prune'])
{
$template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=prune_forum&f=' . $forum_id) . '" alt="cron" width="1" height="1" />');
}

Replace it with the following:

// Do the forum Prune thang - cron type job ...
if ($forum_data['prune_next'] < time() && $forum_data['enable_prune']) {
// MODIFICATION by Dion Designs
// $template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=prune_forum&f=' . $forum_id) . '" alt="cron" width="1" height="1" />');
include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
if ($forum_data['prune_days']) {
$prune_date = time() - ($forum_data['prune_days'] * 86400);
prune($forum_id, 'posted', $prune_date, $forum_data['forum_flags']);
}

if ($forum_data['prune_viewed']) {
$prune_date = time() - ($forum_data['prune_viewed'] * 86400);
prune($forum_id, 'viewed', $prune_date, $forum_data['forum_flags']);
}

$next_prune = time() + ($forum_data['prune_freq'] * 86400);
$sql = 'UPDATE ' . FORUMS_TABLE . "
SET prune_next = $next_prune
WHERE forum_id = $forum_id";
$db->sql_query($sql);

add_log('admin', 'LOG_AUTO_PRUNE', $forum_data['forum_name']);
// END MODIFICATION
}

Save the file. Success! The CRON job in the first post, along with the above modification, eliminates the need for the cron.php "image".

Enjoy!
φ
Posts: 1599
Joined: March 12th 2009, 11:00pm
Location: Uncertain due to momentum
Likes Given: 26
Likes Received: 357
Remove cron.php image and use a real CRON task!
Posted May 27th 2014, 7:55pm
Nice work Dion, Q: Am I correct that it will make the forum much faster to load if you are on shared hosting?

Also just a general note, you will want to check how often you can run cron jobs as some hosts will only let you run one every 1/2 hour.
φ
Posts: 115
Joined: May 26th 2013, 12:17am
Likes Given: 60
Likes Received: 7
1
#4
Remove cron.php image and use a real CRON task!
Posted May 27th 2014, 9:00pm
If you're on shared hosting, running the CRON job every 30 minutes will be fine. My guess is that if you needed to run it more often than that, the host will be contacting you in the very near future to upgrade to a VPS. ;)
φ
Posts: 1599
Joined: March 12th 2009, 11:00pm
Location: Uncertain due to momentum
Likes Given: 26
Likes Received: 357

Who is online

Users browsing this forum: No registered users and 1 guest