diff --git a/composer.json b/composer.json index aa6086f..f3477a5 100644 --- a/composer.json +++ b/composer.json @@ -6,25 +6,9 @@ "homepage": "https://github.com/HealthengineAU/php-resque", "license": "MIT", "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, { "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" + "email": "chris@bigcommerce.com" }, { "email": "thomas.spencer@healthengine.com.au", @@ -36,6 +20,7 @@ "ext-pcntl": "*", "ext-posix": "*", "ext-redis": "*", + "colinmollenhour/credis": "^1.0", "psr/log": "^2.0 || ^3.0" }, "scripts": { diff --git a/lib/Resque.php b/lib/Resque.php index c3bcc1d..56da323 100644 --- a/lib/Resque.php +++ b/lib/Resque.php @@ -28,11 +28,6 @@ class Resque */ protected static $redisDatabase = 0; - /** - * @var string auth of Redis database - */ - protected static $auth; - /** * Given a host/port combination separated by a colon, set it as * the redis server that Resque will talk to. @@ -42,13 +37,11 @@ class Resque * and returns a Resque_Redis instance, or * a nested array of servers with host/port pairs. * @param int $database - * @param string $auth */ - public static function setBackend($server, $database = 0, $auth = null) + public static function setBackend($server, $database = 0) { self::$redisServer = $server; self::$redisDatabase = $database; - self::$auth = $auth; self::$redis = null; } @@ -69,10 +62,6 @@ public static function redis() self::$redis = new Resque_Redis(self::$redisServer, self::$redisDatabase); } - if (!empty(self::$auth)) { - self::$redis->auth(self::$auth); - } - return self::$redis; } @@ -87,6 +76,10 @@ public static function redis() */ public static function fork() { + if(!function_exists('pcntl_fork')) { + return false; + } + // Close the connection to Redis before forking. // This is a workaround for issues phpredis has. self::$redis = null; @@ -220,11 +213,10 @@ public static function size($queue) * @param string $class The name of the class that contains the code to execute the job. * @param array $args Any optional arguments that should be passed when the job is executed. * @param boolean $trackStatus Set to true to be able to monitor the status of a job. - * @param string $prefix The prefix needs to be set for the status key * * @return string|boolean Job ID when the job was created, false if creation was cancelled due to beforeEnqueue */ - public static function enqueue($queue, $class, $args = null, $trackStatus = false, $prefix = "") + public static function enqueue($queue, $class, $args = null, $trackStatus = false) { $id = Resque::generateJobId(); $hookParams = array( @@ -239,7 +231,7 @@ public static function enqueue($queue, $class, $args = null, $trackStatus = fals return false; } - Resque_Job::create($queue, $class, $args, $trackStatus, $id, $prefix); + Resque_Job::create($queue, $class, $args, $trackStatus, $id); Resque_Event::trigger('afterEnqueue', $hookParams); return $id; @@ -270,20 +262,6 @@ public static function queues() return $queues; } - /** - * Retrieve all the items of a queue with Redis - * - * @return array Array of items. - */ - public static function items($queue, $start = 0, $stop = -1) - { - $list = self::redis()->lrange('queue:' . $queue, $start, $stop); - if(!is_array($list)) { - $list = array(); - } - return $list; - } - /** * Remove Items from the queue * Safely moving each item to a temporary queue before processing it @@ -338,7 +316,7 @@ private static function removeItems($queue, $items = array()) /** * matching item - * item can be ['class'] or ['class' => 'id'] or ['class' => {'foo' => 1, 'bar' => 2}] + * item can be ['class'] or ['class' => 'id'] or ['class' => {:foo => 1, :bar => 2}] * @private * * @params string $string redis result in json @@ -360,7 +338,7 @@ private static function matchItem($string, $items) } elseif (is_array($val)) { $decodedArgs = (array)$decoded['args'][0]; if ($decoded['class'] == $key && - count($decodedArgs) > 0 && count(array_diff($decodedArgs, $val)) == 0) { + count($decodedArgs) > 0 && count(array_diff($decodedArgs, $val)) == 0) { return true; } # class name with ID, example: item[0] = ['class' => 'id'] diff --git a/lib/Resque/Failure.php b/lib/Resque/Failure.php index 7c66900..e2e5257 100644 --- a/lib/Resque/Failure.php +++ b/lib/Resque/Failure.php @@ -28,20 +28,6 @@ public static function create($payload, Exception $exception, Resque_Worker $wor new $backend($payload, $exception, $worker, $queue); } - /** - * Create a new failed job on the backend from PHP 7 errors. - * - * @param object $payload The contents of the job that has just failed. - * @param \Error $exception The PHP 7 error generated when the job failed to run. - * @param \Resque_Worker $worker Instance of Resque_Worker that was running this job when it failed. - * @param string $queue The name of the queue that this job was fetched from. - */ - public static function createFromError($payload, Error $exception, Resque_Worker $worker, $queue) - { - $backend = self::getBackend(); - new $backend($payload, $exception, $worker, $queue); - } - /** * Return an instance of the backend for saving job failures. * diff --git a/lib/Resque/Failure/Redis.php b/lib/Resque/Failure/Redis.php index 2ba553d..3e06c34 100644 --- a/lib/Resque/Failure/Redis.php +++ b/lib/Resque/Failure/Redis.php @@ -20,7 +20,7 @@ class Resque_Failure_Redis implements Resque_Failure_Interface public function __construct($payload, $exception, $worker, $queue) { $data = new stdClass(); - $data->failed_at = date('c'); + $data->failed_at = date('D M d H:i:s T Y'); $data->payload = $payload; $data->exception = get_class($exception); $data->error = $exception->getMessage(); diff --git a/lib/Resque/Job.php b/lib/Resque/Job.php index 1cc5344..7e1270e 100755 --- a/lib/Resque/Job.php +++ b/lib/Resque/Job.php @@ -53,12 +53,11 @@ public function __construct($queue, $payload) * @param array $args Any optional arguments that should be passed when the job is executed. * @param boolean $monitor Set to true to be able to monitor the status of a job. * @param string $id Unique identifier for tracking the job. Generated if not supplied. - * @param string $prefix The prefix needs to be set for the status key * * @return string * @throws \InvalidArgumentException */ - public static function create($queue, $class, $args = null, $monitor = false, $id = null, $prefix = "") + public static function create($queue, $class, $args = null, $monitor = false, $id = null) { if (is_null($id)) { $id = Resque::generateJobId(); @@ -70,15 +69,14 @@ public static function create($queue, $class, $args = null, $monitor = false, $i ); } Resque::push($queue, array( - 'class' => $class, - 'args' => array($args), - 'id' => $id, - 'prefix' => $prefix, + 'class' => $class, + 'args' => array($args), + 'id' => $id, 'queue_time' => microtime(true), )); if($monitor) { - Resque_Job_Status::create($id, $prefix); + Resque_Job_Status::create($id); } return $id; @@ -125,28 +123,24 @@ public static function reserveBlocking(array $queues, $timeout = null) * * @param int $status Status constant from Resque_Job_Status indicating the current status of a job. */ - public function updateStatus($status, $result = null) + public function updateStatus($status) { if(empty($this->payload['id'])) { return; } - $statusInstance = new Resque_Job_Status($this->payload['id'], $this->getPrefix()); - $statusInstance->update($status, $result); + $statusInstance = new Resque_Job_Status($this->payload['id']); + $statusInstance->update($status); } /** * Return the status of the current job. * - * @return int|null The status of the job as one of the Resque_Job_Status constants or null if job is not being tracked. + * @return int The status of the job as one of the Resque_Job_Status constants. */ public function getStatus() { - if(empty($this->payload['id'])) { - return null; - } - - $status = new Resque_Job_Status($this->payload['id'], $this->getPrefix()); + $status = new Resque_Job_Status($this->payload['id']); return $status->get(); } @@ -189,18 +183,17 @@ public function getInstance() */ public function perform() { - $result = true; try { Resque_Event::trigger('beforePerform', $this); $instance = $this->getInstance(); - if(is_callable([$instance, 'setUp'])) { + if(method_exists($instance, 'setUp')) { $instance->setUp(); } - $result = $instance->perform(); + $instance->perform(); - if(is_callable([$instance, 'tearDown'])) { + if(method_exists($instance, 'tearDown')) { $instance->tearDown(); } @@ -208,10 +201,10 @@ public function perform() } // beforePerform/setUp have said don't perform this job. Return. catch(Resque_Job_DontPerform $e) { - $result = false; + return false; } - return $result; + return true; } /** @@ -227,21 +220,12 @@ public function fail($exception) )); $this->updateStatus(Resque_Job_Status::STATUS_FAILED); - if ($exception instanceof Error) { - Resque_Failure::createFromError( - $this->payload, - $exception, - $this->worker, - $this->queue - ); - } else { - Resque_Failure::create( - $this->payload, - $exception, - $this->worker, - $this->queue - ); - } + Resque_Failure::create( + $this->payload, + $exception, + $this->worker, + $this->queue + ); Resque_Stat::incr('failed'); Resque_Stat::incr('failed:' . $this->worker); } @@ -252,15 +236,13 @@ public function fail($exception) */ public function recreate() { + $status = new Resque_Job_Status($this->payload['id']); $monitor = false; - if (!empty($this->payload['id'])) { - $status = new Resque_Job_Status($this->payload['id'], $this->getPrefix()); - if($status->isTracking()) { - $monitor = true; - } + if($status->isTracking()) { + $monitor = true; } - return self::create($this->queue, $this->payload['class'], $this->getArguments(), $monitor, null, $this->getPrefix()); + return self::create($this->queue, $this->payload['class'], $this->getArguments(), $monitor); } /** @@ -304,16 +286,4 @@ public function getJobFactory() } return $this->jobFactory; } - - /** - * @return string - */ - private function getPrefix() - { - if (isset($this->payload['prefix'])) { - return $this->payload['prefix']; - } - - return ''; - } } diff --git a/lib/Resque/Job/Status.php b/lib/Resque/Job/Status.php index 3c33a77..6c2c8b5 100644 --- a/lib/Resque/Job/Status.php +++ b/lib/Resque/Job/Status.php @@ -13,11 +13,6 @@ class Resque_Job_Status public const STATUS_FAILED = 3; public const STATUS_COMPLETE = 4; - /** - * @var string The prefix of the job status id. - */ - private $prefix; - /** * @var string The ID of the job this status class refers back to. */ @@ -42,10 +37,9 @@ class Resque_Job_Status * * @param string $id The ID of the job to manage the status for. */ - public function __construct($id, $prefix = '') + public function __construct($id) { $this->id = $id; - $this->prefix = empty($prefix) ? '' : "${prefix}_"; } /** @@ -54,18 +48,14 @@ public function __construct($id, $prefix = '') * * @param string $id The ID of the job to monitor the status of. */ - public static function create($id, $prefix = "") + public static function create($id) { - $status = new self($id, $prefix); $statusPacket = array( - 'status' => self::STATUS_WAITING, + 'status' => self::STATUS_WAITING, 'updated' => time(), 'started' => time(), - 'result' => null, ); - Resque::redis()->set((string) $status, json_encode($statusPacket)); - - return $status; + Resque::redis()->set('job:' . $id . ':status', json_encode($statusPacket)); } /** @@ -94,23 +84,15 @@ public function isTracking() * * @param int The status of the job (see constants in Resque_Job_Status) */ - public function update($status, $result = null) + public function update($status) { - $status = (int) $status; - if(!$this->isTracking()) { return; } - if($status < self::STATUS_WAITING || $status > self::STATUS_COMPLETE) { - return; - } - $statusPacket = array( - 'status' => $status, + 'status' => $status, 'updated' => time(), - 'started' => $this->fetch('started'), - 'result' => $result, ); Resque::redis()->set((string)$this, json_encode($statusPacket)); @@ -123,56 +105,21 @@ public function update($status, $result = null) /** * Fetch the status for the job being monitored. * - * @return mixed False if the status is not being monitored, otherwise the status + * @return mixed False if the status is not being monitored, otherwise the status as * as an integer, based on the Resque_Job_Status constants. */ public function get() { - return $this->status(); - } - - /** - * Fetch the status for the job being monitored. - * - * @return mixed False if the status is not being monitored, otherwise the status - * as an integer, based on the Resque_Job_Status constants. - */ - public function status() - { - return $this->fetch('status'); - } - - /** - * Fetch the last update timestamp of the job being monitored. - * - * @return mixed False if the job is not being monitored, otherwise the - * update timestamp. - */ - public function updated() - { - return $this->fetch('updated'); - } + if(!$this->isTracking()) { + return false; + } - /** - * Fetch the start timestamp of the job being monitored. - * - * @return mixed False if the job is not being monitored, otherwise the - * start timestamp. - */ - public function started() - { - return $this->fetch('started'); - } + $statusPacket = json_decode(Resque::redis()->get((string)$this), true); + if(!$statusPacket) { + return false; + } - /** - * Fetch the result of the job being monitored. - * - * @return mixed False if the job is not being monitored, otherwise the result - * as mixed - */ - public function result() - { - return $this->fetch('result'); + return $statusPacket['status']; } /** @@ -190,35 +137,6 @@ public function stop() */ public function __toString() { - return 'job:' . $this->prefix . $this->id . ':status'; - } - - /** - * Fetch a value from the status packet for the job being monitored. - * - * @return mixed False if the status is not being monitored, otherwise the - * requested value from the status packet. - */ - protected function fetch($value = null) - { - if(!$this->isTracking()) { - return false; - } - - $statusPacket = json_decode(Resque::redis()->get((string)$this), true); - if(!$statusPacket) { - return false; - } - - if(empty($value)) { - return $statusPacket; - } else { - if(isset($statusPacket[$value])) { - return $statusPacket[$value]; - } else { - return null; - } - } - + return 'job:' . $this->id . ':status'; } } diff --git a/lib/Resque/Log.php b/lib/Resque/Log.php index 934ba49..b821ce3 100644 --- a/lib/Resque/Log.php +++ b/lib/Resque/Log.php @@ -18,10 +18,13 @@ public function __construct($verbose = false) /** * Logs with an arbitrary level. * - * @param mixed $level PSR-3 log level constant, or equivalent string - * @param string $message Message to log, may contain a { placeholder } - * @param array $context Variables to replace { placeholder } - * @return null + * @param mixed $level + * @param string|\Stringable $message + * @param mixed[] $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException */ public function log($level, string|\Stringable $message, array $context = []): void { diff --git a/lib/Resque/Redis.php b/lib/Resque/Redis.php index 6af922e..a1968b4 100644 --- a/lib/Resque/Redis.php +++ b/lib/Resque/Redis.php @@ -1,6 +1,6 @@ @@ -29,11 +29,6 @@ class Resque_Redis */ public const DEFAULT_DATABASE = 0; - /** - * @var Redis|RedisCluster - */ - private $driver; - /** * @var array List of all commands in Redis that supply a key as their * first argument. Used to prefix keys with the Resque namespace. @@ -113,59 +108,30 @@ public static function prefix($namespace) * @param string|array $server A DSN or array * @param int $database A database number to select. However, if we find a valid database number in the DSN the * DSN-supplied value will be used instead and this parameter is ignored. - * @param null|\Redis|\RedisCluster $client Optional {@see RedisCluster} or {@see Redis} instantiated by you + * @param object $client Optional Credis_Cluster or Credis_Client instance instantiated by you */ public function __construct($server, $database = null, $client = null) { try { - if (is_object($client)) { + if (is_array($server)) { + $this->driver = new Credis_Cluster($server); + } elseif (is_object($client)) { $this->driver = $client; - } elseif (is_object($server)) { - $this->driver = $server; - } elseif (is_array($server)) { - $this->driver = new \RedisCluster(null, $server); } else { list($host, $port, $dsnDatabase, $user, $password, $options) = self::parseDsn($server); // $user is not used, only $password - // Look for known Redis options - $timeout = isset($options['timeout']) ? floatval($options['timeout']) : null; + // Look for known Credis_Client options + $timeout = isset($options['timeout']) ? intval($options['timeout']) : null; $persistent = isset($options['persistent']) ? $options['persistent'] : ''; $maxRetries = isset($options['max_connect_retries']) ? $options['max_connect_retries'] : 0; - $redis = new \Redis(); - $try = 0; - $connected = false; - $lastException = null; - - while (!$connected && $try <= $maxRetries) { - $try += 1; - - try { - if (is_string($persistent) && trim($persistent) !== '') { - $connected = $redis->pconnect($host, $port, $timeout ?? 0.0, $persistent); - } else { - $connected = $redis->connect($host, $port, $timeout ?? 0.0); - } - } catch (\RedisException $e) { - $lastException = $e; - } - } - - if (!$connected) { - if ($lastException instanceof \RedisException) { - throw $lastException; - } else { - throw new \RedisException('Failed to connect to Redis after exhausting all retries'); - } - } - + $this->driver = new Credis_Client($host, $port, $timeout, $persistent); + $this->driver->setMaxConnectRetries($maxRetries); if ($password) { - $redis->auth($password); + $this->driver->auth($password); } - $this->driver = $redis; - // If we have found a database in our DSN, use it instead of the `$database` // value passed into the constructor. if ($dsnDatabase !== false) { @@ -176,7 +142,7 @@ public function __construct($server, $database = null, $client = null) if ($database !== null) { $this->driver->select($database); } - } catch(\RedisException $e) { + } catch(CredisException $e) { throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e); } } @@ -235,8 +201,9 @@ public static function parseDsn($dsn) $database = intval(preg_replace('/[^0-9]/', '', $parts['path'])); } - // Extract any 'user' values + // Extract any 'user' and 'pass' values $user = isset($parts['user']) ? $parts['user'] : false; + $pass = isset($parts['pass']) ? $parts['pass'] : false; // Convert the query string into an associative array $options = array(); @@ -245,18 +212,6 @@ public static function parseDsn($dsn) parse_str($parts['query'], $options); } - //check 'password-encoding' parameter and extracting password based on encoding - if($options && isset($options['password-encoding']) && $options['password-encoding'] === 'u') { - //extracting urlencoded password - $pass = isset($parts['pass']) ? urldecode($parts['pass']) : false; - } elseif($options && isset($options['password-encoding']) && $options['password-encoding'] === 'b') { - //extracting base64 encoded password - $pass = isset($parts['pass']) ? base64_decode($parts['pass']) : false; - } else { - //extracting pass directly since 'password-encoding' parameter is not present - $pass = isset($parts['pass']) ? $parts['pass'] : false; - } - return array( $parts['host'], $port, @@ -287,8 +242,8 @@ public function __call($name, $args) } } try { - return $this->driver->{$name}(...$args); - } catch (\RedisException $e) { + return $this->driver->__call($name, $args); + } catch (CredisException $e) { throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e); } } diff --git a/lib/Resque/Worker.php b/lib/Resque/Worker.php index b0670f6..62f9a61 100644 --- a/lib/Resque/Worker.php +++ b/lib/Resque/Worker.php @@ -1,5 +1,7 @@ id = $this->hostname . ':' . getmypid() . ':' . implode(',', $this->queues); } - /** - * Set the process prefix of the workers to the given prefix string. - * @param string $prefix The new process prefix - */ - public static function setProcessPrefix($prefix) - { - self::$processPrefix = $prefix; - } - /** * Return all workers known to Resque as instantiated instances. * @return array @@ -172,26 +155,14 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) break; } - // is redis still alive? - try { - if (Resque::redis()->ping() === false) { - throw new \RedisException('redis ping() failed'); - } - } catch (\RedisException $e) { - $this->logger->log(Psr\Log\LogLevel::ERROR, 'redis went away. trying to reconnect'); - Resque::$redis = null; - usleep($interval * 1000000); - continue; - } - // Attempt to find and reserve a job $job = false; if(!$this->paused) { if($blocking === true) { $this->logger->log(Psr\Log\LogLevel::INFO, 'Starting blocking with timeout of {interval}', array('interval' => $interval)); - $this->updateProcLine('Waiting with blocking timeout ' . $interval); + $this->updateProcLine('Waiting for ' . implode(',', $this->queues) . ' with blocking timeout ' . $interval); } else { - $this->updateProcLine('Waiting with interval ' . $interval); + $this->updateProcLine('Waiting for ' . implode(',', $this->queues) . ' with interval ' . $interval); } $job = $this->reserve($blocking, $interval); @@ -209,7 +180,7 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) if($this->paused) { $this->updateProcLine('Paused'); } else { - $this->updateProcLine('Waiting'); + $this->updateProcLine('Waiting for ' . implode(',', $this->queues)); } usleep($interval * 1000000); @@ -224,13 +195,12 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) $this->child = Resque::fork(); - // Forked and we're the child. Or PCNTL is not installed. Run the job. - if ($this->child === 0 || $this->child === false || $this->child === -1) { + // Forked and we're the child. Run the job. + if ($this->child === 0 || $this->child === false) { $status = 'Processing ' . $job->queue . ' since ' . date('Y-m-d H:i:s'); $this->updateProcLine($status); $this->logger->log(Psr\Log\LogLevel::INFO, $status); $this->perform($job); - if ($this->child === 0) { exit(0); } @@ -244,18 +214,11 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) // Wait until the child process finishes before continuing pcntl_wait($status); - - if (pcntl_wifexited($status) !== true) { - $job->fail(new Resque_Job_DirtyExitException('Job exited abnormally')); - } elseif (($exitStatus = pcntl_wexitstatus($status)) !== 0) { + $exitStatus = pcntl_wexitstatus($status); + if($exitStatus !== 0) { $job->fail(new Resque_Job_DirtyExitException( 'Job exited with exit code ' . $exitStatus )); - } else { - if (in_array($job->getStatus(), array(Resque_Job_Status::STATUS_WAITING, Resque_Job_Status::STATUS_RUNNING))) { - $job->updateStatus(Resque_Job_Status::STATUS_COMPLETE); - $this->logger->log(Psr\Log\LogLevel::INFO, 'done ' . $job); - } } } @@ -273,21 +236,16 @@ public function work($interval = Resque::DEFAULT_INTERVAL, $blocking = false) */ public function perform(Resque_Job $job) { - $result = null; try { Resque_Event::trigger('afterFork', $job); - $result = $job->perform(); + $job->perform(); } catch(Exception $e) { - $this->logger->log(Psr\Log\LogLevel::CRITICAL, '{job} has failed {exception}', array('job' => $job, 'exception' => $e)); - $job->fail($e); - return; - } catch(Error $e) { - $this->logger->log(Psr\Log\LogLevel::CRITICAL, '{job} has failed {exception}', array('job' => $job, 'exception' => $e)); + $this->logger->log(Psr\Log\LogLevel::CRITICAL, '{job} has failed {stack}', array('job' => $job, 'stack' => $e)); $job->fail($e); return; } - $job->updateStatus(Resque_Job_Status::STATUS_COMPLETE, $result); + $job->updateStatus(Resque_Job_Status::STATUS_COMPLETE); $this->logger->log(Psr\Log\LogLevel::NOTICE, '{job} has finished', array('job' => $job)); } @@ -298,22 +256,12 @@ public function perform(Resque_Job $job) */ public function reserve($blocking = false, $timeout = null) { - if ($this->hasParent && !posix_kill(posix_getppid(), 0)) { - $this->shutdown(); - return false; - } - $queues = $this->queues(); if(!is_array($queues)) { return; } if($blocking === true) { - if(empty($queues)) { - $this->logger->log(Psr\Log\LogLevel::INFO, 'No queue was found, sleeping for {interval}', array('interval' => $timeout)); - usleep($timeout * 1000000); - return false; - } $job = Resque_Job::reserveBlocking($queues, $timeout); if($job) { $this->logger->log(Psr\Log\LogLevel::INFO, 'Found job on {queue}', array('queue' => $job->queue)); @@ -375,7 +323,7 @@ private function startup() */ private function updateProcLine($status) { - $processTitle = static::$processPrefix . '-' . Resque::VERSION . ' (' . implode(',', $this->queues) . '): ' . $status; + $processTitle = 'resque-' . Resque::VERSION . ': ' . $status; if(function_exists('cli_set_process_title') && PHP_OS !== 'Darwin') { cli_set_process_title($processTitle); } elseif(function_exists('setproctitle')) { @@ -393,7 +341,10 @@ private function updateProcLine($status) */ private function registerSigHandlers() { - pcntl_async_signals(true); + if(!function_exists('pcntl_signal')) { + return; + } + pcntl_signal(SIGTERM, array($this, 'shutDownNow')); pcntl_signal(SIGINT, array($this, 'shutDownNow')); pcntl_signal(SIGQUIT, array($this, 'shutdown')); @@ -442,14 +393,6 @@ public function shutdownNow() $this->killChild(); } - /** - * @return int Child process PID. - */ - public function getChildPID() - { - return $this->child; - } - /** * Kill a forked child job immediately. The job it is processing will not * be completed. @@ -462,7 +405,7 @@ public function killChild() } $this->logger->log(Psr\Log\LogLevel::INFO, 'Killing child at {child}', array('child' => $this->child)); - if(exec('ps -o pid,s -p ' . $this->child, $output, $returnCode) && $returnCode != 1) { + if(exec('ps -o pid,state -p ' . $this->child, $output, $returnCode) && $returnCode != 1) { $this->logger->log(Psr\Log\LogLevel::DEBUG, 'Child {child} found, killing.', array('child' => $this->child)); posix_kill($this->child, SIGKILL); $this->child = null; @@ -505,17 +448,9 @@ public function pruneDeadWorkers() public function workerPids() { $pids = array(); - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - exec('WMIC path win32_process get Processid,Commandline | findstr resque | findstr /V findstr', $cmdOutput); - foreach($cmdOutput as $line) { - $line = preg_replace('/\s+/m', ' ', $line); - list(, , $pids[]) = explode(' ', trim($line), 3); - } - } else { - exec('ps -A -o pid,args | grep [r]esque', $cmdOutput); - foreach($cmdOutput as $line) { - list($pids[], ) = explode(' ', trim($line), 2); - } + exec('ps -A -o pid,command | grep [r]esque', $cmdOutput); + foreach($cmdOutput as $line) { + list($pids[], ) = explode(' ', trim($line), 2); } return $pids; } @@ -526,7 +461,7 @@ public function workerPids() public function registerWorker() { Resque::redis()->sadd('workers', (string)$this); - Resque::redis()->set('worker:' . (string)$this . ':started', date('c')); + Resque::redis()->set('worker:' . (string)$this . ':started', date('D M d H:i:s T Y')); } /** @@ -558,7 +493,7 @@ public function workingOn(Resque_Job $job) $job->updateStatus(Resque_Job_Status::STATUS_RUNNING); $data = json_encode(array( 'queue' => $job->queue, - 'run_at' => date('c'), + 'run_at' => date('D M d H:i:s T Y'), 'payload' => $job->payload )); Resque::redis()->set('worker:' . $job->worker, $data); diff --git a/lib/ResqueScheduler.php b/lib/ResqueScheduler.php deleted file mode 100644 index 958ce07..0000000 --- a/lib/ResqueScheduler.php +++ /dev/null @@ -1,268 +0,0 @@ - -* @copyright (c) 2012 Chris Boulton -* @license http://www.opensource.org/licenses/mit-license.php -*/ -class ResqueScheduler -{ - public const VERSION = "0.1"; - - /** - * Enqueue a job in a given number of seconds from now. - * - * Identical to Resque::enqueue, however the first argument is the number - * of seconds before the job should be executed. - * - * @param int $in Number of seconds from now when the job should be executed. - * @param string $queue The name of the queue to place the job in. - * @param string $class The name of the class that contains the code to execute the job. - * @param array $args Any optional arguments that should be passed when the job is executed. - */ - public static function enqueueIn($in, $queue, $class, array $args = array()) - { - self::enqueueAt(time() + $in, $queue, $class, $args); - } - - /** - * Enqueue a job for execution at a given timestamp. - * - * Identical to Resque::enqueue, however the first argument is a timestamp - * (either UNIX timestamp in integer format or an instance of the DateTime - * class in PHP). - * - * @param DateTime|int $at Instance of PHP DateTime object or int of UNIX timestamp. - * @param string $queue The name of the queue to place the job in. - * @param string $class The name of the class that contains the code to execute the job. - * @param array $args Any optional arguments that should be passed when the job is executed. - */ - public static function enqueueAt($at, $queue, $class, $args = array()) - { - self::validateJob($class, $queue); - - $job = self::jobToHash($queue, $class, $args); - self::delayedPush($at, $job); - - Resque_Event::trigger('afterSchedule', array( - 'at' => $at, - 'queue' => $queue, - 'class' => $class, - 'args' => $args, - )); - } - - /** - * Directly append an item to the delayed queue schedule. - * - * @param DateTime|int $timestamp Timestamp job is scheduled to be run at. - * @param array $item Hash of item to be pushed to schedule. - */ - public static function delayedPush($timestamp, $item) - { - $timestamp = self::getTimestamp($timestamp); - $redis = Resque::redis(); - $redis->rpush('delayed:' . $timestamp, json_encode($item)); - - $redis->zadd('delayed_queue_schedule', $timestamp, $timestamp); - } - - /** - * Get the total number of jobs in the delayed schedule. - * - * @return int Number of scheduled jobs. - */ - public static function getDelayedQueueScheduleSize() - { - return (int)Resque::redis()->zcard('delayed_queue_schedule'); - } - - /** - * Get the number of jobs for a given timestamp in the delayed schedule. - * - * @param DateTime|int $timestamp Timestamp - * @return int Number of scheduled jobs. - */ - public static function getDelayedTimestampSize($timestamp) - { - $timestamp = self::toTimestamp($timestamp); - return Resque::redis()->llen('delayed:' . $timestamp, $timestamp); - } - - /** - * Remove a delayed job from the queue - * - * note: you must specify exactly the same - * queue, class and arguments that you used when you added - * to the delayed queue - * - * also, this is an expensive operation because all delayed keys have tobe - * searched - * - * @param $queue - * @param $class - * @param $args - * @return int number of jobs that were removed - */ - public static function removeDelayed($queue, $class, $args) - { - $destroyed = 0; - $item = json_encode(self::jobToHash($queue, $class, $args)); - $redis = Resque::redis(); - - foreach($redis->keys('delayed:*') as $key) { - $key = $redis->removePrefix($key); - $destroyed += $redis->lrem($key, 0, $item); - } - - return $destroyed; - } - - /** - * removed a delayed job queued for a specific timestamp - * - * note: you must specify exactly the same - * queue, class and arguments that you used when you added - * to the delayed queue - * - * @param $timestamp - * @param $queue - * @param $class - * @param $args - * @return mixed - */ - public static function removeDelayedJobFromTimestamp($timestamp, $queue, $class, $args) - { - $key = 'delayed:' . self::getTimestamp($timestamp); - $item = json_encode(self::jobToHash($queue, $class, $args)); - $redis = Resque::redis(); - $count = $redis->lrem($key, 0, $item); - self::cleanupTimestamp($key, $timestamp); - - return $count; - } - - /** - * Generate hash of all job properties to be saved in the scheduled queue. - * - * @param string $queue Name of the queue the job will be placed on. - * @param string $class Name of the job class. - * @param array $args Array of job arguments. - */ - - private static function jobToHash($queue, $class, $args) - { - return array( - 'class' => $class, - 'args' => array($args), - 'queue' => $queue, - ); - } - - /** - * If there are no jobs for a given key/timestamp, delete references to it. - * - * Used internally to remove empty delayed: items in Redis when there are - * no more jobs left to run at that timestamp. - * - * @param string $key Key to count number of items at. - * @param int $timestamp Matching timestamp for $key. - */ - private static function cleanupTimestamp($key, $timestamp) - { - $timestamp = self::getTimestamp($timestamp); - $redis = Resque::redis(); - - if ($redis->llen($key) == 0) { - $redis->del($key); - $redis->zrem('delayed_queue_schedule', $timestamp); - } - } - - /** - * Convert a timestamp in some format in to a unix timestamp as an integer. - * - * @param DateTime|int $timestamp Instance of DateTime or UNIX timestamp. - * @return int Timestamp - * @throws ResqueScheduler_InvalidTimestampException - */ - private static function getTimestamp($timestamp) - { - if ($timestamp instanceof DateTime) { - $timestamp = $timestamp->getTimestamp(); - } - - if ((int)$timestamp != $timestamp) { - throw new ResqueScheduler_InvalidTimestampException( - 'The supplied timestamp value could not be converted to an integer.' - ); - } - - return (int)$timestamp; - } - - /** - * Find the first timestamp in the delayed schedule before/including the timestamp. - * - * Will find and return the first timestamp upto and including the given - * timestamp. This is the heart of the ResqueScheduler that will make sure - * that any jobs scheduled for the past when the worker wasn't running are - * also queued up. - * - * @param DateTime|int $timestamp Instance of DateTime or UNIX timestamp. - * Defaults to now. - * @return int|false UNIX timestamp, or false if nothing to run. - */ - public static function nextDelayedTimestamp($at = null) - { - if ($at === null) { - $at = time(); - } else { - $at = self::getTimestamp($at); - } - - $items = Resque::redis()->zrangebyscore('delayed_queue_schedule', '-inf', $at, array('limit' => array(0, 1))); - if (!empty($items)) { - return $items[0]; - } - - return false; - } - - /** - * Pop a job off the delayed queue for a given timestamp. - * - * @param DateTime|int $timestamp Instance of DateTime or UNIX timestamp. - * @return array Matching job at timestamp. - */ - public static function nextItemForTimestamp($timestamp) - { - $timestamp = self::getTimestamp($timestamp); - $key = 'delayed:' . $timestamp; - - $item = json_decode(Resque::redis()->lpop($key), true); - - self::cleanupTimestamp($key, $timestamp); - return $item; - } - - /** - * Ensure that supplied job class/queue is valid. - * - * @param string $class Name of job class. - * @param string $queue Name of queue. - * @throws Resque_Exception - */ - private static function validateJob($class, $queue) - { - if (empty($class)) { - throw new Resque_Exception('Jobs must be given a class.'); - } elseif (empty($queue)) { - throw new Resque_Exception('Jobs must be put in a queue.'); - } - - return true; - } -} diff --git a/lib/ResqueScheduler/InvalidTimestampException.php b/lib/ResqueScheduler/InvalidTimestampException.php deleted file mode 100644 index 9806a47..0000000 --- a/lib/ResqueScheduler/InvalidTimestampException.php +++ /dev/null @@ -1,10 +0,0 @@ - -* @copyright (c) 2012 Chris Boulton -* @license http://www.opensource.org/licenses/mit-license.php -*/ -class ResqueScheduler_InvalidTimestampException extends Resque_Exception {} diff --git a/lib/ResqueScheduler/Worker.php b/lib/ResqueScheduler/Worker.php deleted file mode 100644 index 502b7e7..0000000 --- a/lib/ResqueScheduler/Worker.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @copyright (c) 2012 Chris Boulton - * @license http://www.opensource.org/licenses/mit-license.php - */ -class ResqueScheduler_Worker -{ - public const LOG_NONE = 0; - public const LOG_NORMAL = 1; - public const LOG_VERBOSE = 2; - - /** - * @var int Current log level of this worker. - */ - public $logLevel = 0; - - /** - * @var int Interval to sleep for between checking schedules. - */ - protected $interval = 5; - - /** - * @var boolean True if on the next iteration, the worker should shutdown. - */ - private $shutdown = false; - - /** - * @var boolean True if this worker is paused. - */ - private $paused = false; - - /** - * The primary loop for a worker. - * - * Every $interval (seconds), the scheduled queue will be checked for jobs - * that should be pushed to Resque. - * - * @param int $interval How often to check schedules. - */ - public function work($interval = null) - { - if ($interval !== null) { - $this->interval = $interval; - } - - $this->updateProcLine('Starting'); - $this->registerSigHandlers(); - - while (true) { - if($this->shutdown) { - break; - } - if(!$this->paused) { - $this->handleDelayedItems(); - } - $this->sleep(); - } - } - - /** - * Handle delayed items for the next scheduled timestamp. - * - * Searches for any items that are due to be scheduled in Resque - * and adds them to the appropriate job queue in Resque. - * - * @param DateTime|int $timestamp Search for any items up to this timestamp to schedule. - */ - public function handleDelayedItems($timestamp = null) - { - while (($oldestJobTimestamp = ResqueScheduler::nextDelayedTimestamp($timestamp)) !== false) { - $this->updateProcLine('Processing Delayed Items'); - $this->enqueueDelayedItemsForTimestamp($oldestJobTimestamp); - } - } - - /** - * Schedule all of the delayed jobs for a given timestamp. - * - * Searches for all items for a given timestamp, pulls them off the list of - * delayed jobs and pushes them across to Resque. - * - * @param DateTime|int $timestamp Search for any items up to this timestamp to schedule. - */ - public function enqueueDelayedItemsForTimestamp($timestamp) - { - $item = null; - while ($item = ResqueScheduler::nextItemForTimestamp($timestamp)) { - $this->log('queueing ' . $item['class'] . ' in ' . $item['queue'] . ' [delayed]'); - - Resque_Event::trigger('beforeDelayedEnqueue', array( - 'queue' => $item['queue'], - 'class' => $item['class'], - 'args' => $item['args'], - )); - - $payload = array_merge(array($item['queue'], $item['class']), $item['args']); - call_user_func_array('Resque::enqueue', $payload); - } - } - - /** - * Sleep for the defined interval. - */ - protected function sleep() - { - sleep($this->interval); - } - - /** - * Update the status of the current worker process. - * - * On supported systems (with the PECL proctitle module installed), update - * the name of the currently running process to indicate the current state - * of a worker. - * - * @param string $status The updated process title. - */ - private function updateProcLine($status) - { - if(function_exists('setproctitle')) { - setproctitle('resque-scheduler-' . ResqueScheduler::VERSION . ': ' . $status); - } - } - - /** - * Output a given log message to STDOUT. - * - * @param string $message Message to output. - */ - public function log($message) - { - if($this->logLevel == self::LOG_NORMAL) { - fwrite(STDOUT, "*** " . $message . "\n"); - } elseif($this->logLevel == self::LOG_VERBOSE) { - fwrite(STDOUT, "** [" . date('H:i:s Y-m-d') . "] " . $message . "\n"); - } - } - - /** - * Register signal handlers that a worker should respond to. - * - * TERM: Shutdown after the current timestamp was processed. - * INT: Shutdown after the current timestamp was processed. - * QUIT: Shutdown after the current timestamp was processed. - */ - private function registerSigHandlers() - { - pcntl_async_signals(true); - pcntl_signal(SIGTERM, array($this, 'shutdown')); - pcntl_signal(SIGINT, array($this, 'shutdown')); - pcntl_signal(SIGQUIT, array($this, 'shutdown')); - pcntl_signal(SIGUSR2, array($this, 'pauseProcessing')); - pcntl_signal(SIGCONT, array($this, 'unPauseProcessing')); - - $this->log('Registered signals'); - } - - public function shutdown() - { - $this->log('Shutting down'); - $this->shutdown = true; - } - - /** - * Signal handler callback for USR2, pauses processing. - */ - public function pauseProcessing() - { - $this->log('USR2 received; pausing processing'); - $this->paused = true; - } - - /** - * Signal handler callback for CONT, resume processing. - */ - public function unPauseProcessing() - { - $this->log('CONT received; resuming processing'); - $this->paused = false; - } -} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 18d8667..6ee7db0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,10 @@ parameters: ignoreErrors: + - + message: "#^Access to an undefined property Resque_Worker\\:\\:\\$hasParent\\.$#" + count: 2 + path: bin/resque + - message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" count: 2 @@ -46,22 +51,42 @@ parameters: path: bin/resque - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 7 + message: "#^Access to constant LOG_NORMAL on an unknown class ResqueScheduler_Worker\\.$#" + count: 1 path: bin/resque-scheduler - - message: "#^Only booleans are allowed in an if condition, string\\|false given\\.$#" + message: "#^Access to constant LOG_VERBOSE on an unknown class ResqueScheduler_Worker\\.$#" + count: 1 + path: bin/resque-scheduler + + - + message: "#^Access to property \\$logLevel on an unknown class ResqueScheduler_Worker\\.$#" count: 2 path: bin/resque-scheduler - - message: "#^Only booleans are allowed in \\|\\|, int\\<0, max\\>\\|false given on the left side\\.$#" + message: "#^Call to method work\\(\\) on an unknown class ResqueScheduler_Worker\\.$#" count: 1 path: bin/resque-scheduler - - message: "#^Parameter \\#1 \\$interval of method ResqueScheduler_Worker\\:\\:work\\(\\) expects int\\|null, int\\|string given\\.$#" + message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" + count: 7 + path: bin/resque-scheduler + + - + message: "#^Instantiated class ResqueScheduler_Worker not found\\.$#" + count: 1 + path: bin/resque-scheduler + + - + message: "#^Only booleans are allowed in an if condition, string\\|false given\\.$#" + count: 2 + path: bin/resque-scheduler + + - + message: "#^Only booleans are allowed in \\|\\|, int\\<0, max\\>\\|false given on the left side\\.$#" count: 1 path: bin/resque-scheduler @@ -90,11 +115,6 @@ parameters: count: 1 path: lib/Resque.php - - - message: "#^Call to an undefined method Resque_Redis\\:\\:lrange\\(\\)\\.$#" - count: 1 - path: lib/Resque.php - - message: "#^Call to an undefined method Resque_Redis\\:\\:rpop\\(\\)\\.$#" count: 1 @@ -152,7 +172,7 @@ parameters: - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 3 + count: 2 path: lib/Resque.php - @@ -186,27 +206,12 @@ parameters: path: lib/Resque.php - - message: "#^Method Resque\\:\\:generateJobId\\(\\) has no return type specified\\.$#" + message: "#^Method Resque\\:\\:fork\\(\\) should return int but returns false\\.$#" count: 1 path: lib/Resque.php - - message: "#^Method Resque\\:\\:items\\(\\) has parameter \\$queue with no type specified\\.$#" - count: 1 - path: lib/Resque.php - - - - message: "#^Method Resque\\:\\:items\\(\\) has parameter \\$start with no type specified\\.$#" - count: 1 - path: lib/Resque.php - - - - message: "#^Method Resque\\:\\:items\\(\\) has parameter \\$stop with no type specified\\.$#" - count: 1 - path: lib/Resque.php - - - - message: "#^Method Resque\\:\\:items\\(\\) return type has no value type specified in iterable type array\\.$#" + message: "#^Method Resque\\:\\:generateJobId\\(\\) has no return type specified\\.$#" count: 1 path: lib/Resque.php @@ -275,11 +280,6 @@ parameters: count: 1 path: lib/Resque.php - - - message: "#^Static property Resque\\:\\:\\$auth \\(string\\) does not accept string\\|null\\.$#" - count: 1 - path: lib/Resque.php - - message: "#^Static property Resque\\:\\:\\$redis \\(Resque_Redis\\) does not accept null\\.$#" count: 2 @@ -310,11 +310,6 @@ parameters: count: 1 path: lib/Resque/Failure.php - - - message: "#^Method Resque_Failure\\:\\:createFromError\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/Resque/Failure.php - - message: "#^Method Resque_Failure\\:\\:getBackend\\(\\) should return object but returns string\\.$#" count: 1 @@ -357,7 +352,7 @@ parameters: - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 5 + count: 3 path: lib/Resque/Job.php - @@ -391,7 +386,7 @@ parameters: path: lib/Resque/Job.php - - message: "#^Method Resque_Job\\:\\:getStatus\\(\\) should return int\\|null but returns mixed\\.$#" + message: "#^Method Resque_Job\\:\\:getStatus\\(\\) should return int but returns mixed\\.$#" count: 1 path: lib/Resque/Job.php @@ -405,21 +400,11 @@ parameters: count: 1 path: lib/Resque/Job.php - - - message: "#^Method Resque_Job\\:\\:updateStatus\\(\\) has parameter \\$result with no type specified\\.$#" - count: 1 - path: lib/Resque/Job.php - - message: "#^Parameter \\#1 \\$payload of static method Resque_Failure\\:\\:create\\(\\) expects object, array given\\.$#" count: 1 path: lib/Resque/Job.php - - - message: "#^Parameter \\#1 \\$payload of static method Resque_Failure\\:\\:createFromError\\(\\) expects object, array given\\.$#" - count: 1 - path: lib/Resque/Job.php - - message: "#^Parameter \\#2 \\$timeout of static method Resque\\:\\:blpop\\(\\) expects int, int\\|null given\\.$#" count: 1 @@ -521,17 +506,7 @@ parameters: path: lib/Resque/Job/Status.php - - message: "#^Cannot access offset mixed on mixed\\.$#" - count: 1 - path: lib/Resque/Job/Status.php - - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 2 - path: lib/Resque/Job/Status.php - - - - message: "#^Method Resque_Job_Status\\:\\:__construct\\(\\) has parameter \\$prefix with no type specified\\.$#" + message: "#^Cannot access offset 'status' on mixed\\.$#" count: 1 path: lib/Resque/Job/Status.php @@ -540,16 +515,6 @@ parameters: count: 1 path: lib/Resque/Job/Status.php - - - message: "#^Method Resque_Job_Status\\:\\:create\\(\\) has parameter \\$prefix with no type specified\\.$#" - count: 1 - path: lib/Resque/Job/Status.php - - - - message: "#^Method Resque_Job_Status\\:\\:fetch\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: lib/Resque/Job/Status.php - - message: "#^Method Resque_Job_Status\\:\\:stop\\(\\) has no return type specified\\.$#" count: 1 @@ -560,11 +525,6 @@ parameters: count: 1 path: lib/Resque/Job/Status.php - - - message: "#^Method Resque_Job_Status\\:\\:update\\(\\) has parameter \\$result with no type specified\\.$#" - count: 1 - path: lib/Resque/Job/Status.php - - message: "#^Method Resque_Job_Status\\:\\:update\\(\\) has parameter \\$status with no type specified\\.$#" count: 1 @@ -595,28 +555,18 @@ parameters: count: 1 path: lib/Resque/Log.php - - - message: "#^Method Resque_Log\\:\\:log\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" - count: 1 - path: lib/Resque/Log.php - - - - message: "#^PHPDoc tag @return with type null is incompatible with native type void\\.$#" - count: 1 - path: lib/Resque/Log.php - - message: "#^Property Resque_Log\\:\\:\\$verbose has no type specified\\.$#" count: 1 path: lib/Resque/Log.php - - message: "#^Call to an undefined method Redis\\|RedisCluster\\:\\:select\\(\\)\\.$#" - count: 1 + message: "#^Access to an undefined property Resque_Redis\\:\\:\\$driver\\.$#" + count: 4 path: lib/Resque/Redis.php - - message: "#^Call to function base64_decode\\(\\) requires parameter \\#2 to be set\\.$#" + message: "#^Call to an undefined method object\\:\\:select\\(\\)\\.$#" count: 1 path: lib/Resque/Redis.php @@ -625,11 +575,6 @@ parameters: count: 2 path: lib/Resque/Redis.php - - - message: "#^Call to function is_object\\(\\) with array\\|string will always evaluate to false\\.$#" - count: 1 - path: lib/Resque/Redis.php - - message: "#^Cannot access offset 'host' on array\\{scheme\\?\\: string, host\\?\\: string, port\\?\\: int\\<0, 65535\\>, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\|false\\.$#" count: 1 @@ -670,21 +615,11 @@ parameters: count: 1 path: lib/Resque/Redis.php - - - message: "#^Only booleans are allowed in &&, array\\ given on the left side\\.$#" - count: 2 - path: lib/Resque/Redis.php - - message: "#^Property Resque_Redis\\:\\:\\$keyCommands type has no value type specified in iterable type array\\.$#" count: 1 path: lib/Resque/Redis.php - - - message: "#^Variable method call on Redis\\|RedisCluster\\.$#" - count: 1 - path: lib/Resque/Redis.php - - message: "#^Call to an undefined method Resque_Redis\\:\\:decrby\\(\\)\\.$#" count: 1 @@ -710,11 +645,6 @@ parameters: count: 2 path: lib/Resque/Worker.php - - - message: "#^Binary operation \"\\.\" between 'done ' and object\\|true results in an error\\.$#" - count: 1 - path: lib/Resque/Worker.php - - message: "#^Call to an undefined method Resque_Redis\\:\\:del\\(\\)\\.$#" count: 3 @@ -725,11 +655,6 @@ parameters: count: 1 path: lib/Resque/Worker.php - - - message: "#^Call to an undefined method Resque_Redis\\:\\:ping\\(\\)\\.$#" - count: 1 - path: lib/Resque/Worker.php - - message: "#^Call to an undefined method Resque_Redis\\:\\:sadd\\(\\)\\.$#" count: 1 @@ -762,7 +687,7 @@ parameters: - message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" - count: 3 + count: 2 path: lib/Resque/Worker.php - @@ -782,16 +707,6 @@ parameters: - message: "#^Cannot call method fail\\(\\) on object\\|true\\.$#" - count: 2 - path: lib/Resque/Worker.php - - - - message: "#^Cannot call method getStatus\\(\\) on object\\|true\\.$#" - count: 1 - path: lib/Resque/Worker.php - - - - message: "#^Cannot call method updateStatus\\(\\) on object\\|true\\.$#" count: 1 path: lib/Resque/Worker.php @@ -800,11 +715,6 @@ parameters: count: 2 path: lib/Resque/Worker.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 1 - path: lib/Resque/Worker.php - - message: "#^Method Resque_Worker\\:\\:__construct\\(\\) has parameter \\$queues with no value type specified in iterable type array\\.$#" count: 1 @@ -890,11 +800,6 @@ parameters: count: 1 path: lib/Resque/Worker.php - - - message: "#^Method Resque_Worker\\:\\:setProcessPrefix\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/Resque/Worker.php - - message: "#^Method Resque_Worker\\:\\:shutdown\\(\\) has no return type specified\\.$#" count: 1 @@ -965,11 +870,6 @@ parameters: count: 2 path: lib/Resque/Worker.php - - - message: "#^Only numeric types are allowed in \\*, int\\|null given on the left side\\.$#" - count: 1 - path: lib/Resque/Worker.php - - message: "#^PHPDoc tag @param for parameter \\$job with type object is not subtype of native type Resque_Job\\.$#" count: 1 @@ -985,11 +885,6 @@ parameters: count: 1 path: lib/Resque/Worker.php - - - message: "#^Parameter \\#1 \\$string of function trim expects string, string\\|null given\\.$#" - count: 1 - path: lib/Resque/Worker.php - - message: "#^Property Resque_Worker\\:\\:\\$child \\(int\\) does not accept null\\.$#" count: 2 @@ -1006,7 +901,7 @@ parameters: path: lib/Resque/Worker.php - - message: "#^Static property Resque\\:\\:\\$redis \\(Resque_Redis\\) does not accept null\\.$#" + message: "#^Strict comparison using \\=\\=\\= between 0 and 0 will always evaluate to true\\.$#" count: 1 path: lib/Resque/Worker.php @@ -1014,233 +909,3 @@ parameters: message: "#^Strict comparison using \\=\\=\\= between int\\\\|int\\<1, max\\> and false will always evaluate to false\\.$#" count: 1 path: lib/Resque/Worker.php - - - - message: "#^Unsafe access to private property Resque_Worker\\:\\:\\$processPrefix through static\\:\\:\\.$#" - count: 1 - path: lib/Resque/Worker.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:del\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:keys\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:llen\\(\\)\\.$#" - count: 2 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:lpop\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:lrem\\(\\)\\.$#" - count: 2 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:rpush\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:zadd\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:zcard\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:zrangebyscore\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined method Resque_Redis\\:\\:zrem\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Call to an undefined static method ResqueScheduler\\:\\:toTimestamp\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Casting to int something that's already int\\.$#" - count: 2 - path: lib/ResqueScheduler.php - - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 3 - path: lib/ResqueScheduler.php - - - - message: "#^Dynamic call to static method Resque_Redis\\:\\:removePrefix\\(\\)\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:cleanupTimestamp\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:delayedPush\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:delayedPush\\(\\) has parameter \\$item with no value type specified in iterable type array\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:enqueueAt\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:enqueueAt\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:enqueueIn\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:enqueueIn\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:jobToHash\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:jobToHash\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:nextDelayedTimestamp\\(\\) has parameter \\$at with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:nextItemForTimestamp\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:nextItemForTimestamp\\(\\) should return array but returns mixed\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayed\\(\\) has parameter \\$args with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayed\\(\\) has parameter \\$class with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayed\\(\\) has parameter \\$queue with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayedJobFromTimestamp\\(\\) has parameter \\$args with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayedJobFromTimestamp\\(\\) has parameter \\$class with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayedJobFromTimestamp\\(\\) has parameter \\$queue with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:removeDelayedJobFromTimestamp\\(\\) has parameter \\$timestamp with no type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler\\:\\:validateJob\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$timestamp$#" - count: 1 - path: lib/ResqueScheduler.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:enqueueDelayedItemsForTimestamp\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:handleDelayedItems\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:log\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:pauseProcessing\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:registerSigHandlers\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:shutdown\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:sleep\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:unPauseProcessing\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:updateProcLine\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php - - - - message: "#^Method ResqueScheduler_Worker\\:\\:work\\(\\) has no return type specified\\.$#" - count: 1 - path: lib/ResqueScheduler/Worker.php diff --git a/test/Resque/Tests/EventTest.php b/test/Resque/Tests/EventTest.php index 9a76400..60c9508 100644 --- a/test/Resque/Tests/EventTest.php +++ b/test/Resque/Tests/EventTest.php @@ -11,7 +11,7 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase { private $callbacksHit = array(); - protected function setUp(): void + public function setUp(): void { Test_Job::$called = false; @@ -21,7 +21,7 @@ protected function setUp(): void $this->worker->registerWorker(); } - protected function tearDown(): void + public function tearDown(): void { Resque_Event::clearListeners(); $this->callbacksHit = array(); @@ -166,7 +166,7 @@ public function assertValidEventCallback($function, $job) $this->assertEquals($args[0], 'somevar'); } - public function afterEnqueueEventCallback($class, $args, $id, $queue) + public function afterEnqueueEventCallback($args, $class, $id, $queue) { $this->callbacksHit[] = __FUNCTION__; $this->assertEquals('Test_Job', $class); diff --git a/test/Resque/Tests/JobStatusTest.php b/test/Resque/Tests/JobStatusTest.php index 1cf6a56..1a123de 100644 --- a/test/Resque/Tests/JobStatusTest.php +++ b/test/Resque/Tests/JobStatusTest.php @@ -14,7 +14,7 @@ class Resque_Tests_JobStatusTest extends Resque_Tests_TestCase */ protected $worker; - protected function setUp(): void + public function setUp(): void { parent::setUp(); diff --git a/test/Resque/Tests/JobTest.php b/test/Resque/Tests/JobTest.php index 680bc51..ea261ae 100644 --- a/test/Resque/Tests/JobTest.php +++ b/test/Resque/Tests/JobTest.php @@ -12,7 +12,7 @@ class Resque_Tests_JobTest extends Resque_Tests_TestCase { protected $worker; - protected function setUp(): void + public function setUp(): void { parent::setUp(); @@ -29,17 +29,16 @@ public function testJobCanBeQueued() public function testRedisErrorThrowsExceptionOnJobCreation() { - $mockRedis = $this->createMock(\Redis::class); - $mockRedis->method('connect')->willReturn(true); - $mockRedis->expects($this->any())->method('sAdd') - ->will($this->throwException(new \RedisException('failure'))); + $mockCredis = $this->getMockBuilder('Credis_Client') + ->onlyMethods(['connect', '__call']) + ->getMock(); + $mockCredis->expects($this->any())->method('__call') + ->will($this->throwException(new CredisException('failure'))); - Resque::setBackend(function ($database) use ($mockRedis) { - return new Resque_Redis('localhost:6379', $database, $mockRedis); + Resque::setBackend(function ($database) use ($mockCredis) { + return new Resque_Redis('localhost:6379', $database, $mockCredis); }); - $this->expectException(Resque_RedisException::class); - Resque::enqueue('jobs', 'This is a test'); } @@ -59,9 +58,7 @@ public function testObjectArgumentsCannotBePassedToJob() { $args = new stdClass(); $args->test = 'somevalue'; - - $this->expectException(\InvalidArgumentException::class); - + $this->expectException(InvalidArgumentException::class); Resque::enqueue('jobs', 'Test_Job', $args); } @@ -137,9 +134,7 @@ public function testJobWithoutPerformMethodThrowsException() Resque::enqueue('jobs', 'Test_Job_Without_Perform_Method'); $job = $this->worker->reserve(); $job->worker = $this->worker; - $this->expectException(Resque_Exception::class); - $job->perform(); } @@ -148,9 +143,7 @@ public function testInvalidJobThrowsException() Resque::enqueue('jobs', 'Invalid_Job'); $job = $this->worker->reserve(); $job->worker = $this->worker; - $this->expectException(Resque_Exception::class); - $job->perform(); } @@ -411,36 +404,12 @@ public function testDoNotUseFactoryToGetInstance() 'args' => array(array()) ); $job = new Resque_Job('jobs', $payload); - $factory = $this->createMock('Resque_Job_FactoryInterface'); - $testJob = $this->createMock('Resque_JobInterface'); + $factory = $this->createMock(Resque_Job_FactoryInterface::class); + $testJob = $this->createMock(Resque_JobInterface::class); $factory->expects(self::never())->method('create')->will(self::returnValue($testJob)); $instance = $job->getInstance(); $this->assertInstanceOf('Resque_JobInterface', $instance); } - - public function testJobStatusIsNullIfIdMissingFromPayload() - { - $payload = array( - 'class' => 'Some_Job_Class', - 'args' => null - ); - $job = new Resque_Job('jobs', $payload); - $this->assertEquals(null, $job->getStatus()); - } - - public function testJobCanBeRecreatedFromLegacyPayload() - { - $payload = array( - 'class' => 'Some_Job_Class', - 'args' => null - ); - $job = new Resque_Job('jobs', $payload); - $job->recreate(); - $newJob = Resque_Job::reserve('jobs'); - $this->assertEquals('jobs', $newJob->queue); - $this->assertEquals('Some_Job_Class', $newJob->payload['class']); - $this->assertNotNull($newJob->payload['id']); - } } class Some_Job_Class implements Resque_JobInterface diff --git a/test/Resque/Tests/RedisTest.php b/test/Resque/Tests/RedisTest.php index ceb1b9b..fa29af9 100644 --- a/test/Resque/Tests/RedisTest.php +++ b/test/Resque/Tests/RedisTest.php @@ -11,17 +11,16 @@ class Resque_Tests_RedisTest extends Resque_Tests_TestCase { public function testRedisExceptionsAreSurfaced() { - $mockRedis = $this->createMock(\Redis::class); - $mockRedis->method('connect')->willReturn(true); - $mockRedis->expects($this->any())->method('ping') - ->will($this->throwException(new \RedisException('failure'))); + $mockCredis = $this->getMockBuilder('Credis_Client') + ->onlyMethods(['connect', '__call']) + ->getMock(); + $mockCredis->expects($this->any())->method('__call') + ->will($this->throwException(new CredisException('failure'))); - Resque::setBackend(function ($database) use ($mockRedis) { - return new Resque_Redis('localhost:6379', $database, $mockRedis); + Resque::setBackend(function ($database) use ($mockCredis) { + return new Resque_Redis('localhost:6379', $database, $mockCredis); }); - $this->expectException(Resque_RedisException::class); - Resque::redis()->ping(); } @@ -190,8 +189,7 @@ public function testParsingValidDsnString($dsn, $expected) */ public function testParsingBogusDsnStringThrowsException($dsn) { - $this->expectException(\InvalidArgumentException::class); - + $this->expectException(InvalidArgumentException::class); Resque_Redis::parseDsn($dsn); } } diff --git a/test/Resque/Tests/TestCase.php b/test/Resque/Tests/TestCase.php index 8d7afdb..3cac5eb 100644 --- a/test/Resque/Tests/TestCase.php +++ b/test/Resque/Tests/TestCase.php @@ -19,15 +19,11 @@ public static function setUpBeforeClass(): void date_default_timezone_set('UTC'); } - protected function setUp(): void + public function setUp(): void { $config = file_get_contents(REDIS_CONF); preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches); - - $redis = new \Redis(); - $redis->connect('localhost', $matches[1]); - - $this->redis = $redis; + $this->redis = new Credis_Client('localhost', $matches[1]); Resque::setBackend('redis://localhost:' . $matches[1]); diff --git a/test/Resque/Tests/WorkerTest.php b/test/Resque/Tests/WorkerTest.php index 1a14615..5a08566 100644 --- a/test/Resque/Tests/WorkerTest.php +++ b/test/Resque/Tests/WorkerTest.php @@ -290,15 +290,4 @@ public function testBlockingListPop() $this->assertEquals(2, $i); } - - public function testWorkerFailsSegmentationFaultJob() - { - Resque::enqueue('jobs', 'Test_Infinite_Recursion_Job'); - - $worker = new Resque_Worker('jobs'); - $worker->setLogger(new Resque_Log()); - $worker->work(0); - - $this->assertEquals(1, Resque_Stat::get('failed')); - } } diff --git a/test/bootstrap.php b/test/bootstrap.php index deae478..4f7561f 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -7,6 +7,14 @@ * @license http://www.opensource.org/licenses/mit-license.php */ +ini_set('display_errors', 1); +ini_set('error_reporting', E_ALL); + +// Throw all error levels as exceptions. +set_error_handler(function (int $level, string $message, string $file, int $line): bool { + throw new \ErrorException($message, 0, $level, $file, $line); +}); + $loader = require __DIR__ . '/../vendor/autoload.php'; $loader->add('Resque_Tests', __DIR__); @@ -106,24 +114,6 @@ public function perform() } } -/** - * This job exits the forked worker process, which simulates the job being (forever) in progress, - * so that we can verify the state of the system for "running jobs". Does not work on a non-forking OS. - * - * CAUTION Use this test job only with Worker::work, i.e. only when you actually trigger the fork in tests. - */ -class InProgress_Job -{ - public function perform() - { - if(!function_exists('pcntl_fork')) { - // We can't lose the worker on a non-forking OS. - throw new Failing_Job_Exception('Do not use InProgress_Job for tests on non-forking OS!'); - } - exit(0); - } -} - class Test_Job_Without_Perform_Method {} class Test_Job_With_SetUp @@ -131,7 +121,7 @@ class Test_Job_With_SetUp public static $called = false; public $args = false; - public function setUp() + public function setUp(): void { self::$called = true; } @@ -147,16 +137,8 @@ class Test_Job_With_TearDown public function perform() {} - public function tearDown() + public function tearDown(): void { self::$called = true; } } - -class Test_Infinite_Recursion_Job -{ - public function perform() - { - $this->perform(); - } -}