Archive: Namaste PHP AMQP framework v1.0 (2017-2020)
952 days continuous production uptime, 40k+ tp/s single node. Original corpo Bitbucket history not included — clean archive commit.
This commit is contained in:
366
classes/gacATWrapper.class.inc
Normal file
366
classes/gacATWrapper.class.inc
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This is a wrapper class for the AT daemon. It was plagiarized from:
|
||||
*
|
||||
* https://github.com/treffynnon/PHP-at-Job-Queue-Wrapper/blob/master/lib/Treffynnon/At/Wrapper.php
|
||||
*
|
||||
* Because the original (author's) version uses exceptions for error reporting. I've re-tooled the original code,
|
||||
* eliminating the exception processing, making the output logging align with Namaste's logging formats, and closing
|
||||
* access publicly to all methods except the intended public function.
|
||||
*
|
||||
* @author treffynnon@php.net Simon Holywell
|
||||
* @author mike@givingassistant.org
|
||||
* @version 1.0
|
||||
*
|
||||
*
|
||||
* HISTORY:
|
||||
* ========
|
||||
* 06-07-17 mks port from original source
|
||||
* 08-17-20 mks DB-168: code review
|
||||
*
|
||||
*/
|
||||
class gacATWrapper
|
||||
{
|
||||
protected static string $binary = 'at'; // path to the AT binary
|
||||
protected static string $addRegex = '/^job (\d+) at ([\w\d- :]+)$/'; // regexp to fetch the current job listings
|
||||
protected static array $addMap = array( // regexp mapping -> descriptive names
|
||||
1 => 'job_number',
|
||||
2 => 'date',
|
||||
);
|
||||
// regexp for fetching queue info
|
||||
protected static string $queueRegex = '/^(\d+)\s+([\w\d- :]+) (\w) ([\w-]+)$/';
|
||||
// another regexp matching for queue data
|
||||
protected static array $queueMap = array(
|
||||
1 => 'job_number',
|
||||
2 => 'date',
|
||||
3 => 'queue',
|
||||
4 => 'user',
|
||||
);
|
||||
|
||||
protected static string $res = 'CRON: ';
|
||||
protected static string $pipeTo = '2>&1'; // redirects STDERR to STDOUT (redundant)
|
||||
|
||||
protected static array $atSwitches = array( // supported AT options list
|
||||
'queue' => '-q',
|
||||
'list_queue' => '-l',
|
||||
'file' => '-f',
|
||||
'remove' => '-d',
|
||||
);
|
||||
|
||||
/**
|
||||
* cmd() -- public static function
|
||||
*
|
||||
* @users self::addCommand
|
||||
*
|
||||
* @param $command
|
||||
* @param $time
|
||||
* @param null $queue
|
||||
* @return mixed
|
||||
*/
|
||||
static public function cmd($command, $time, $queue = null)
|
||||
{
|
||||
return self::addCommand($command, $time, $queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @uses self::addFile
|
||||
*
|
||||
* @param $file
|
||||
* @param $time
|
||||
* @param null $queue
|
||||
* @return mixed
|
||||
*/
|
||||
static public function file($file, $time, $queue = null)
|
||||
{
|
||||
return self::addFile($file, $time, $queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @uses self::listQueue
|
||||
*
|
||||
* @param null $queue
|
||||
* @return mixed
|
||||
*/
|
||||
static public function lq($queue = null)
|
||||
{
|
||||
return self::listQueue($queue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a job to the `at` queue
|
||||
* @param string $command
|
||||
* @param string $time see `man at`
|
||||
* @param string $queue a-zA-Z see `man at`
|
||||
* @return mixed
|
||||
*/
|
||||
static private function addCommand($command, $time, $queue = null)
|
||||
{
|
||||
$command = self::escape($command);
|
||||
$time = self::escape($time);
|
||||
$exec_string = "echo '$command' | " . self::$binary;
|
||||
if(null !== $queue) {
|
||||
$exec_string .= ' ' . self::$atSwitches['queue'] . " {$queue[0]}";
|
||||
}
|
||||
$exec_string .= " $time ";
|
||||
return self::addJob($exec_string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a file job to the `at` queue
|
||||
* @param string $file Full path to the file to be executed
|
||||
* @param string $time see `man at`
|
||||
* @param string $queue a-zA-Z see `man at`
|
||||
* @return mixed
|
||||
*/
|
||||
static private function addFile($file, $time, $queue = null)
|
||||
{
|
||||
$file = self::escape($file);
|
||||
$time = self::escape($time);
|
||||
$exec_string = self::$binary . ' ' . self::$atSwitches['file'] . " $file";
|
||||
if(null !== $queue) {
|
||||
$exec_string .= ' ' . self::$atSwitches['queue'] . " {$queue[0]}";
|
||||
}
|
||||
$exec_string .= " $time ";
|
||||
return self::addJob($exec_string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a list of the jobs currently in the queue. If you do not specify
|
||||
* a queue to look at then it will return all jobs in all queues.
|
||||
* @param string $queue
|
||||
* @return array of Job objects
|
||||
* @return mixed
|
||||
*/
|
||||
static private function listQueue($queue = null)
|
||||
{
|
||||
$exec_string = self::$binary . ' ' . self::$atSwitches['list_queue'];
|
||||
if(null !== $queue) {
|
||||
$exec_string .= ' ' . self::$atSwitches['queue'] . " {$queue[0]}";
|
||||
}
|
||||
$result = self::exec($exec_string);
|
||||
return self::transform($result, 'queue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a job by job number
|
||||
* @param int $job_number
|
||||
* @return Boolean
|
||||
*/
|
||||
static public function removeJob($job_number)
|
||||
{
|
||||
$rc = true;
|
||||
if (empty($job_number)) {
|
||||
$hdr = sprintf(INFO_LOC, __METHOD__, __LINE__);
|
||||
consoleLog(static::$res, CON_ERROR, $hdr . RES_ATW . ERROR_DATA_INPUT_EMPTY . STRING_JOB_NUMBER);
|
||||
return(false);
|
||||
}
|
||||
$job_number = self::escape($job_number);
|
||||
// $exec_string = self::$binary . ' ' . self::$atSwitches['remove'] . " $job_number";
|
||||
$exec_string = 'atrm ' . $job_number;
|
||||
$output = self::exec($exec_string);
|
||||
if(count($output)) {
|
||||
$rc = false;
|
||||
foreach ($output as $errorMessage) {
|
||||
$hdr = sprintf(INFO_LOC, __METHOD__, __LINE__);
|
||||
consoleLog(static::$res, CON_ERROR, $hdr . $errorMessage);
|
||||
}
|
||||
echo getDateTime() . CON_ERROR . RES_ATW . $output[0] . PHP_EOL;
|
||||
}
|
||||
return($rc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a job to the at queue and return the
|
||||
* @param string $job_exec_string
|
||||
* @return mixed
|
||||
*/
|
||||
static private function addJob($job_exec_string)
|
||||
{
|
||||
$output = self::exec($job_exec_string);
|
||||
return (count($output) == 1) ? $output[0] : $output[1];
|
||||
// $job = self::transform($output);
|
||||
// if(!count($job)) {
|
||||
// $logger = new gacErrorLogger();
|
||||
// $logger->warn('failed to add job to the queue. Exec command: ' . $job_exec_string);
|
||||
// $logger->__destruct();
|
||||
// }
|
||||
// return reset($job);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform the output of `at` into an array of objects
|
||||
* @param array $output_array
|
||||
* @param string $type Is this an add or list we are transforming?
|
||||
* @return array An array of Job objects
|
||||
*/
|
||||
static private function transform(array $output_array, string $type = 'add'):array
|
||||
{
|
||||
$jobs = array();
|
||||
// Get the appropriate regex class property for the type
|
||||
// of `at` switch/command being run at this point in time.
|
||||
$regex = $type . 'Regex';
|
||||
$regex = self::$$regex;
|
||||
$map = $type .'Map';
|
||||
$map = self::$$map;
|
||||
|
||||
foreach($output_array as $line) {
|
||||
$matches = array();
|
||||
@preg_match($regex, $line, $matches);
|
||||
if(count($matches) > count($map)) {
|
||||
$jobs[] = self::mapJob($matches, $map);
|
||||
}
|
||||
}
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map the details matched with the regex to descriptively named properties
|
||||
* in a new Job object
|
||||
* @param array $details
|
||||
* @param array $map
|
||||
* @return Job
|
||||
*/
|
||||
static private function mapJob($details, $map)
|
||||
{
|
||||
$Job = new Job();
|
||||
foreach($details as $key => $detail) {
|
||||
if(isset($map[$key])) {
|
||||
$Job->$map[$key] = $detail;
|
||||
}
|
||||
}
|
||||
return $Job;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape a string that will be passed to exec
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
static private function escape($string)
|
||||
{
|
||||
return escapeshellcmd($string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run the command via exec() and return each line of the output as an
|
||||
* array
|
||||
* @param string $string
|
||||
* @return array Each line of output is an element in the array
|
||||
*/
|
||||
static private function exec($string)
|
||||
{
|
||||
$output = array();
|
||||
$string .= ' ' . self::$pipeTo;
|
||||
exec($string, $output);
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A simple class for storing a jobs details and some methods for manipulating
|
||||
* it. A job model if you will.
|
||||
*
|
||||
* @author Simon Holywell <treffynnon@php.net>
|
||||
* @version 16.11.2010
|
||||
*/
|
||||
class Job {
|
||||
/**
|
||||
* Data store for the job details
|
||||
* @var array
|
||||
*/
|
||||
public array $data = array();
|
||||
protected string $res = 'CRON: ';
|
||||
protected /** @noinspection PhpMissingFieldTypeInspection */ $date;
|
||||
|
||||
/**
|
||||
* Magic method to set a value in the $data
|
||||
* property of the class
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Magic method to get a value in the $data property
|
||||
* of the class
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
$logger = new gacErrorLogger();
|
||||
$trace = debug_backtrace();
|
||||
$logger->warn("Undefined property via __get(): $name in $trace[0]['file'] . on line $trace[0]['line']");
|
||||
$logger->__destruct();
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Magic method to check for the existence of an
|
||||
* index in the $data property of the class
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->data[$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Magic method to unset an index in the $data property
|
||||
* of the class
|
||||
* @param string $name
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
unset($this->data[$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove this job from the queue
|
||||
*/
|
||||
public function remove() {
|
||||
if(isset($this->job_number)) {
|
||||
gacATWrapper::removeJob((int)$this->job_number);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DateTime object for date and time extracted from
|
||||
* the output of `at`
|
||||
* @example echo $job->date()->format('d-m-Y');
|
||||
* @uses DateTime
|
||||
* @return DateTime A PHP DateTime object
|
||||
*/
|
||||
public function date(): ?DateTime
|
||||
{
|
||||
try {
|
||||
return new DateTime($this->date);
|
||||
} catch (Exception | TypeError $t) {
|
||||
$hdr = sprintf(INFO_LOC, __METHOD__, __LINE__);
|
||||
consoleLog($this->res, CON_ERROR, $hdr . $t->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user