TimeTrex/classes/modules/cron/Cron.class.php

324 lines
11 KiB
PHP
Raw Permalink Normal View History

2022-12-13 07:10:06 +01:00
<?php
/*********************************************************************************
*
* TimeTrex is a Workforce Management program developed by
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
*
* You should have received a copy of the GNU Affero General Public License along
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
*
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
*
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by TimeTrex".
*
********************************************************************************/
/**
* @package Modules\Cron
*/
class Cron {
static protected $limits = [
'minute' => [ 'min' => 0, 'max' => 59 ],
'hour' => [ 'min' => 0, 'max' => 23 ],
'day_of_month' => [ 'min' => 1, 'max' => 31 ],
'month' => [ 'min' => 1, 'max' => 12 ],
'day_of_week' => [ 'min' => 0, 'max' => 7 ],
];
/**
* @param $name
* @param int $interval
* @return array|bool
*/
static function getOptions( $name, $interval = 1 ) {
$all_array_option = [ '*' => TTi18n::getText( '-- All --' ) ];
$retval = false;
switch ( $name ) {
case 'minute':
for ( $i = 0; $i <= 59; $i += $interval ) {
$retval[$i] = $i;
}
$retval = Misc::prependArray( $all_array_option, $retval );
break;
case 'hour':
for ( $i = 0; $i <= 23; $i += $interval ) {
$retval[$i] = $i;
}
$retval = Misc::prependArray( $all_array_option, $retval );
break;
case 'day_of_month':
$retval = Misc::prependArray( $all_array_option, TTDate::getDayOfMonthArray() );
break;
case 'month':
$retval = Misc::prependArray( $all_array_option, TTDate::getMonthOfYearArray() );
break;
case 'day_of_week':
$retval = Misc::prependArray( $all_array_option, TTDate::getDayOfWeekArray() );
break;
}
return $retval;
}
/**
* @param $value_arr
* @param $type
* @return bool
*/
static function isValidLimit( $value_arr, $type ) {
if ( isset( self::$limits[$type] ) ) {
$limit_arr = self::$limits[$type];
} else {
Debug::text( 'Type is invalid: ' . $type, __FILE__, __LINE__, __METHOD__, 10 );
return false;
}
if ( is_array( $value_arr ) && is_array( $limit_arr ) && count( $value_arr ) > 0 ) {
//Debug::Arr($value_arr, 'Value Arr: ', __FILE__, __LINE__, __METHOD__, 10);
//Debug::Arr($limit_arr, 'Limit Arr: ', __FILE__, __LINE__, __METHOD__, 10);
foreach ( $value_arr as $value ) {
if ( $value == '*' ) {
$retval = true;
} else {
if ( $value >= $limit_arr['min'] && $value <= $limit_arr['max'] ) {
$retval = true;
} else {
return false;
}
}
}
return $retval;
}
return false;
}
/**
* @param $arr
* @param $type
* @return bool|string
*/
static function arrayToScheduleString( $arr, $type ) {
if ( !is_array( $arr ) ) {
if ( $arr !== 0 && $arr !== '0' && empty( $arr ) ) {
$arr = '*';
}
$arr = [ $arr ];
}
//If any of the array entries is '*', just return that as the string and ignore everything else.
// Use STRICT=TRUE on in_array() check, otherwise (int)0 matches '*' search.
if ( is_array( $arr ) && in_array( '*', $arr, true ) === true ) {
return '*';
} else {
if ( is_array( $arr ) ) {
sort( $arr );
$retval = implode( ',', array_unique( $arr ) );
//Debug::Arr( $retval, 'Final String: ', __FILE__, __LINE__, __METHOD__, 10 );
return $retval;
}
}
return false;
}
/**
* Parses any column into a complete list of entries.
* ie: converts: 0-59 to an array of: 0, 1, 2, 3, 4, 5, 6, ...
* 0-2, 16, 18 to array of 0, 1, 2, 16, 18
* <star>/2 to array of 0, 2, 4, 6, 8, ...
* @param $str
* @param $type
* @return array
*/
static function parseScheduleString( $str, $type ) {
if ( $str !== 0 && $str !== '0' && empty( $str ) ) {
$str = '*';
}
$split_str = explode( ',', $str );
if ( count( $split_str ) == 0 ) {
//Debug::text('Schedule String DOES NOT have multiple commas: '. count($split_str), __FILE__, __LINE__, __METHOD__, 10);
$split_str = [ $split_str ];
} //else { Debug::text('Schedule String has multiple commas: '. count($split_str), __FILE__, __LINE__, __METHOD__, 10);
$retarr = [];
$limit_options = self::$limits;
foreach ( $split_str as $str_atom ) {
if ( strpos( $str_atom, '-' ) !== false ) {
//Debug::text('Schedule atom has basic range: '. $str_atom, __FILE__, __LINE__, __METHOD__, 10);
//Found basic range
//get Min/Max of range
$str_atom_range = explode( '-', $str_atom );
$retarr = array_merge( $retarr, range( $str_atom_range[0], $str_atom_range[1] ) );
unset( $str_atom_range );
} else if ( strpos( $str_atom, '/' ) !== false ) {
//Debug::text('Schedule atom has advanced range: '. $str_atom, __FILE__, __LINE__, __METHOD__, 10);
//Found basic range
//get Min/Max of range
$str_atom_range = explode( '/', $str_atom );
$retarr = array_merge( $retarr, range( $limit_options[$type]['min'], $limit_options[$type]['max'], $str_atom_range[1] ) );
unset( $str_atom_range );
} else {
//No Range found
//Debug::text('Schedule atom does not have range: '. $str_atom, __FILE__, __LINE__, __METHOD__, 10);
if ( trim( $str_atom ) == '*' ) {
//Debug::text('Found Full Range!: '. $str_atom, __FILE__, __LINE__, __METHOD__, 10);
$retarr = array_merge( $retarr, range( $limit_options[$type]['min'], $limit_options[$type]['max'] ) );
} else {
//Debug::text('Singleton: '. $str_atom, __FILE__, __LINE__, __METHOD__, 10);
$retarr[] = (int)$str_atom;
}
}
}
rsort( $retarr );
$retarr = array_values( array_unique( $retarr ) ); //Unique and rekey array so index is consecutive. This prevents problems with the dropdown box.
//Debug::Arr($retarr, 'Final Array: ', __FILE__, __LINE__, __METHOD__, 10);
return $retarr;
}
/**
* @param $min_col
* @param $hour_col
* @param $dom_col
* @param $month_col
* @param $dow_col
* @param int $epoch EPOCH
* @return false|int|null
*/
static function getNextScheduleDate( $min_col, $hour_col, $dom_col, $month_col, $dow_col, $epoch = null ) {
if ( $epoch == '' ) {
$epoch = 0;
}
$month_arr = self::parseScheduleString( $month_col, 'month' );
$day_of_month_arr = self::parseScheduleString( $dom_col, 'day_of_month' );
$day_of_week_arr = self::parseScheduleString( $dow_col, 'day_of_week' );
$hour_arr = self::parseScheduleString( $hour_col, 'hour' );
$minute_arr = self::parseScheduleString( $min_col, 'minute' );
$retval = $epoch;
$i = 0;
while ( $i < 500 ) { //Prevent infinite loop
$date_arr = getdate( $retval );
$i++;
//Order from minute to month, least granular to most granular.
if ( !in_array( $date_arr['minutes'], $minute_arr ) ) {
$retval = TTDate::incrementDate( $retval, 1, 'minute' );
//Debug::text(' Minute: Retval: '. TTDate::getDate('DATE+TIME', $retval) .' Current Epoch: '. TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
continue;
}
if ( !in_array( $date_arr['hours'], $hour_arr ) ) {
$retval = TTDate::incrementDate( $retval, 1, 'hour' );
//Debug::text(' Hour: Retval: '. TTDate::getDate('DATE+TIME', $retval) .' Current Epoch: '. TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
continue;
}
if ( !in_array( $date_arr['mday'], $day_of_month_arr ) || !in_array( $date_arr['wday'], $day_of_week_arr ) ) {
$retval = TTDate::getBeginDayEpoch( TTDate::incrementDate( $retval, 1, 'day' ) );
//Debug::text(' Day: Retval: '. TTDate::getDate('DATE+TIME', $retval) .' Current Epoch: '. TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
continue;
}
if ( !in_array( $date_arr['mon'], $month_arr ) ) {
$retval = TTDate::getBeginDayEpoch( TTDate::incrementDate( $retval, 1, 'month' ) );
//Debug::text(' Month: Retval: '. TTDate::getDate('DATE+TIME', $retval) .' Current Epoch: '. TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
continue;
}
//Debug::text(' None: Retval: '. TTDate::getDate('DATE+TIME', $retval) .' Current Epoch: '. TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
//Halt the loop...
break;
}
Debug::text( ' Next Scheduled Date: ' . TTDate::getDate( 'DATE+TIME', $retval ) . ' Based on Current Epoch: ' . TTDate::getDate( 'DATE+TIME', $epoch ), __FILE__, __LINE__, __METHOD__, 10 );
//Debug::text(' JOB is NOT SCHEDULED TO RUN YET!', __FILE__, __LINE__, __METHOD__, 10);
return $retval;
}
/**
* @param $min_col
* @param $hour_col
* @param $dom_col
* @param $month_col
* @param $dow_col
* @param int $epoch EPOCH
* @param int $last_run_date EPOCH
* @return bool
*/
static function isScheduledToRun( $min_col, $hour_col, $dom_col, $month_col, $dow_col, $epoch = null, $last_run_date = null ) {
//Debug::text('Checking if Cron Job is scheduled to run: '. self::getName(), __FILE__, __LINE__, __METHOD__, 10);
if ( $epoch == '' ) {
//$epoch = time();
$epoch = 0;
}
//Debug::text('Checking if Cron Job is scheduled to run: '. self::getName(), __FILE__, __LINE__, __METHOD__, 10);
if ( $last_run_date == '' ) {
$last_run_date = 0;
}
if ( $last_run_date > ( time() + 86400 ) ) {
Debug::text( ' Last Run Date is in the future: ' . TTDate::getDate( 'DATE+TIME', $last_run_date ) . ' assuming this is an error and forcing it to run now...', __FILE__, __LINE__, __METHOD__, 10 );
$last_run_date = 0;
}
$next_schedule_epoch = self::getNextScheduleDate( $min_col, $hour_col, $dom_col, $month_col, $dow_col, $last_run_date );
if ( $next_schedule_epoch < $epoch ) {
Debug::text( ' JOB is SCHEDULED TO RUN NOW!', __FILE__, __LINE__, __METHOD__, 10 );
return true;
}
Debug::text( ' JOB is NOT scheduled to run right now...', __FILE__, __LINE__, __METHOD__, 10 );
return false;
}
}
?>