Files
pathfinder/app/Cron/AbstractCron.php
2019-12-20 14:16:55 +01:00

211 lines
6.7 KiB
PHP

<?php
/**
* Created by PhpStorm.
* User: Exodus 4D
* Date: 17.06.2018
* Time: 12:13
*/
namespace Exodus4D\Pathfinder\Cron;
use Exodus4D\Pathfinder\Lib\Format\Number;
use Exodus4D\Pathfinder\Model\Pathfinder;
abstract class AbstractCron {
/**
* default "base" log message text
* -> generic information data
*/
const LOG_TEXT_BASE = '%4s/%-4s %6s done, %5s total, %8s peak, %9s exec';
/**
* default max_execution_time for cronJobs
* -> should be less then execution period
*/
const DEFAULT_MAX_EXECUTION_TIME = 50;
/**
* default threshold time in seconds before a running script (e.g. a large loop) should stop
* -> so there is some time or e.g. logging,... left
*/
const DEFAULT_EXECUTION_TIME_THRESHOLD = 3;
/**
* started jobs
* @var Pathfinder\CronModel[]
*/
protected $activeCron = [];
/**
* disables log file write entry for some cronJobs
* -> either job runs too frequently, or no relevant data available for logging
* @var array
*/
protected $logDisabled = [];
/**
* set max execution time for cronJbs
* -> Default CLI execution time is 0 == infinite!
* php.ini settings are ignored! http://php.net/manual/en/info.configuration.php#ini.max-execution-time
* @param int $time
*/
protected function setMaxExecutionTime(int $time = self::DEFAULT_MAX_EXECUTION_TIME){
ini_set('max_execution_time', $time);
}
/**
* get max execution time
* -> 0 means == infinite!
* @return int
*/
protected function getMaxExecutionTime() : int {
return (int)ini_get('max_execution_time');
}
/**
* checks execution time of a "long" running script
* -> returns false if execution time is close to maxExecutionTime
* @param float $timeTotalStart
* @param float|null $timeCheck
* @param int $timeThreshold
* @return bool
*/
protected function isExecutionTimeLeft(float $timeTotalStart, float $timeCheck = null, int $timeThreshold = self::DEFAULT_EXECUTION_TIME_THRESHOLD) : bool {
$timeLeft = true;
if($timeTotalMax = $this->getMaxExecutionTime()){
$timeTotalMaxThreshold = $timeTotalStart + $timeTotalMax - $timeThreshold;
$timeCheck = $timeCheck ? : microtime(true);
if($timeCheck >= $timeTotalMaxThreshold){
$timeLeft = false;
}
}
return $timeLeft;
}
/**
* log cronjob exec state on start
* @param string $job
* @param bool $logging
*/
protected function logStart(string $job, bool $logging = true){
$this->setMaxExecutionTime();
$cron = \Exodus4D\Pathfinder\Lib\Cron::instance();
if(isset($cron->jobs[$job])){
// set "start" date for current cronjob
$jobConf = $cron->getJobDataFromConf($cron->jobs[$job]);
$jobConf['lastExecStart'] = $_SERVER['REQUEST_TIME_FLOAT'];
if(($cronModel = $cron->registerJob($job, $jobConf)) instanceof Pathfinder\CronModel){
$this->activeCron[$job] = $cronModel;
}
}
if(!$logging){
$this->logDisabled[] = $job;
}
}
/**
* log cronjob exec state on finish
* @param string $job
* @param int $total
* @param int $count
* @param int $importCount
* @param int $offset
* @param string $logText
*/
protected function logEnd(string $job, int $total = 0, int $count = 0, int $importCount = 0, int $offset = 0, string $logText = ''){
$execEnd = microtime(true);
$memPeak = memory_get_peak_usage();
$state = [
'total' => $total,
'count' => $count,
'importCount' => $importCount,
'offset' => $offset,
'loop' => 1,
'percent' => $total ? round(100 / $total * ($count + $offset), 1) : 100
];
if(isset($this->activeCron[$job])){
if($lastState = $this->activeCron[$job]->lastExecState){
if(isset($lastState['loop']) && $offset){
$state['loop'] = (int)$lastState['loop'] + 1;
}
}
$jobConf = [
'lastExecEnd' => $execEnd,
'lastExecMemPeak' => $memPeak,
'lastExecState' => $state
];
$this->activeCron[$job]->setData($jobConf);
$this->activeCron[$job]->save();
unset($this->activeCron[$job]);
}
if(!in_array($job, $this->logDisabled)){
$this->writeLog($job, $memPeak, $execEnd, $state, $logText);
}
}
/**
* get either CLI GET params OR
* check for params from last run -> incremental import
* @param string $job
* @return array
*/
protected function getParams(string $job) : array {
$params = [];
// check for CLI GET params
$f3 = \Base::instance();
if($getParams = (array)$f3->get('GET')){
if(isset($getParams['offset'])){
$params['offset'] = (int)$getParams['offset'];
}
if(isset($getParams['length']) && (int)$getParams['length'] > 0){
$params['length'] = (int)$getParams['length'];
}
}
// .. or check for logged params from last exec state (DB entry)
if(empty($params) && isset($this->activeCron[$job])){
if($lastState = $this->activeCron[$job]->lastExecState){
if(isset($lastState['offset'])){
$params['offset'] = (int)$lastState['offset'];
}
if(isset($lastState['count'])){
$params['offset'] = (int)$params['offset'] + (int)$lastState['count'];
}
if(isset($lastState['loop'])){
$params['loop'] = (int)$lastState['loop'];
}
}
}
return $params;
}
/**
* write log file for $job
* @param string $job
* @param int $memPeak
* @param float $execEnd
* @param array $state
* @param string $logText for custom text
*/
private function writeLog(string $job, int $memPeak = 0, float $execEnd = 0, array $state = [], string $logText = ''){
$percent = number_format($state['percent'], 1) . '%';
$duration = number_format(round($execEnd - $_SERVER['REQUEST_TIME_FLOAT'], 3), 3) . 's';
$log = new \Log('cron_' . $job . '.log');
$text = sprintf(self::LOG_TEXT_BASE,
$state['count'], $state['importCount'], $percent, $state['total'],
Number::instance()->bytesToString($memPeak), $duration
);
$text .= $logText ? $logText: '';
$log->write($text);
}
}