fannie/classlib2.0/FannieTask.php
<?php
/*******************************************************************************
Copyright 2013 Whole Foods Co-op
This file is part of CORE-POS.
IT CORE is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
IT CORE 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 General Public License for more details.
You should have received a copy of the GNU General Public License
in the file license.txt along with IT CORE; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*********************************************************************************/
/**
@class FannieTask
Base class for scheduled tasks
*/
class FannieTask
{
public $name = 'Fannie Task';
public $description = 'Information about the task';
public $default_schedule = array(
'min' => 0,
'hour' => 0,
'day' => 1,
'month' => 1,
'weekday' => '*',
);
public $schedulable = true;
/**
Normally the start and stop time of a task is
automatically logged. This can be helpful to
verify a task a) actually ran and b) completed
without crashing. However for tasks that run
very frequently this can generate excess log
noise.
*/
public $log_start_stop = true;
protected $error_threshold = 0;
protected $config = null;
protected $logger = null;
protected $options = array();
protected $arguments = array();
protected $test_mode = false;
public function setThreshold($t)
{
$this->error_threshold = $t;
}
public function setConfig(FannieConfig $fc)
{
$this->config = $fc;
}
public function setLogger($fl)
{
$this->logger = $fl;
}
public function setOptions($o)
{
$this->options = $o;
}
public function setArguments($a)
{
$this->arguments = $a;
}
public function testMode($t)
{
$this->test_mode = $t;
}
/**
Implement task functionality here
*/
public function run()
{
}
private function psrSeverity($s)
{
switch($s) {
case 0:
return 'emergency';
case 1:
return 'alert';
case 2:
return 'critical';
case 3:
return 'error';
case 4:
return 'warning';
case 5:
return 'notice';
case 6:
return 'info';
case 7:
default:
return 'debug';
}
}
/**
Write message to log and if necessary raise it to stderr
to trigger an email
@param $str message string
@param $severity [optional, default 6/info] message importance
@return empty string
*/
public function cronMsg($str, $severity=6)
{
$info = new ReflectionClass($this);
$msg = date('r').': '.$info->getName().': '.$str."\n";
$log_level = $this->psrSeverity($severity);
$this->logger->log($log_level, $info->getName() . ': ' . $str);
// raise message into stderr
// nb. 99 means "Never email on error"
if ($this->error_threshold != 99) {
if ($severity <= $this->error_threshold) {
file_put_contents('php://stderr', $msg, FILE_APPEND);
}
}
return '';
}
/**
getopt style parsing. not fully posix compliant.
@param $argv [array] of options and arguments
@return [array]
- options [array] of option names and values
- arguments [array] of non-option arguments
Example:
php FannieTask.php SomeTask -v --verbose -h 1 --host=1 something else
lazyGetOpt returns
- options
"-v" => true
"--verbose" => true
"-h" => 1
"--host" => 1
- arguments
0 => "something"
1 => "else"
*/
public function lazyGetOpt($argv)
{
$options = array();
$nonopt = array();
for ($i=0; $i<count($argv); $i++) {
$arg = $argv[$i];
if ($this->isValueOption($arg)) {
$options[$this->getOptionName($arg)] = $this->getOptionValue($arg);
} elseif ($this->isBareOption($arg)) {
if ($i+1 < count($argv) && substr($argv[$i+1],0,1) != '-') {
$options[$this->getOptionName($arg)] = $argv[$i+1];
$i++;
} else {
$options[$this->getOptionName($arg)] = true;
}
} else {
$nonopt[] = $arg;
}
}
return array(
'options' => $options,
'arguments' => $nonopt,
);
}
private function isBareOption($opt)
{
return preg_match('/^-\w$/', $opt) || preg_match('/^--\w+$/', $opt);
}
private function isValueOption($opt)
{
return preg_match('/^-\w=.+$/', $opt) || preg_match('/^--\w+=.+$/', $opt);
}
private function getOptionName($opt)
{
$opt = ltrim($opt, '-');
$parts = explode('=', $opt, 2);
return $parts[0];
}
private function getOptionValue($opt)
{
$parts = explode('=', $opt, 2);
return $parts[1];
}
protected function getLockFile()
{
$dir = sys_get_temp_dir();
$class = str_replace('\\', '_', get_class($this));
return $dir . DIRECTORY_SEPARATOR . $class . '.lock';
}
protected function isLocked()
{
return file_exists($this->getLockFile());
}
protected function lock()
{
$file = $this->getLockFile();
$lock = fopen($file, 'w');
fwrite($lock, date('Y-m-d H:i:s'));
fclose($lock);
chmod($file, 0666);
}
protected function unlock()
{
unlink($this->getLockFile());
}
}
if (php_sapi_name() === 'cli' && basename($_SERVER['PHP_SELF']) == basename(__FILE__)) {
if ($argc < 2) {
echo "Usage: php FannieTask.php <Task Class Name>\n";
return 1;
}
include(dirname(__FILE__).'/../config.php');
include(dirname(__FILE__).'/FannieAPI.php');
$config = FannieConfig::factory();
$logger = FannieLogger::factory();
COREPOS\common\ErrorHandler::setLogger($logger);
COREPOS\common\ErrorHandler::setErrorHandlers();
// prepopulate autoloader
$preload = FannieAPI::listModules('FannieTask');
$class = $argv[1];
if (!class_exists($class)) {
echo "Error: class '$class' does not exist\n";
return 1;
}
$obj = new $class();
if (!is_a($obj, 'FannieTask')) {
echo "Error: invalid class. Must be subclass of FannieTask\n";
return 1;
}
if (is_numeric($config->get('TASK_THRESHOLD'))) {
$obj->setThreshold($config->get('TASK_THRESHOLD'));
}
$obj->setConfig($config);
$obj->setLogger($logger);
/**
Parse & set extra options and arguments
*/
if ($argc > 2) {
$remainder = array_slice($argv, 2);
$parsed = $obj->lazyGetOpt($remainder);
$obj->setOptions($parsed['options']);
$obj->setArguments($parsed['arguments']);
}
if ($obj->log_start_stop) {
$logger->info('Starting task: ' . $class);
}
$obj->run();
if ($obj->log_start_stop) {
$logger->info('Finished task: ' . $class);
}
}