public function get(array $query, $runningResetDuration, $waitDurationInMillis = 3000, $pollDurationInMillis = 200)
{
if (!is_int($runningResetDuration)) {
throw new \InvalidArgumentException('$runningResetDuration was not an int');
}
if (!is_int($waitDurationInMillis)) {
throw new \InvalidArgumentException('$waitDurationInMillis was not an int');
}
if (!is_int($pollDurationInMillis)) {
throw new \InvalidArgumentException('$pollDurationInMillis was not an int');
}
if ($pollDurationInMillis < 0) {
$pollDurationInMillis = 0;
}
//reset stuck messages
$this->collection->updateMany(['running' => true, 'resetTimestamp' => ['$lte' => new \MongoDB\BSON\UTCDateTime((int) (microtime(true) * 1000))]], ['$set' => ['running' => false]]);
$completeQuery = ['running' => false];
foreach ($query as $key => $value) {
if (!is_string($key)) {
throw new \InvalidArgumentException('key in $query was not a string');
}
$completeQuery["payload.{$key}"] = $value;
}
$completeQuery['earliestGet'] = ['$lte' => new \MongoDB\BSON\UTCDateTime((int) (microtime(true) * 1000))];
$resetTimestamp = time() + $runningResetDuration;
//ints overflow to floats
if (!is_int($resetTimestamp)) {
$resetTimestamp = $runningResetDuration > 0 ? self::MONGO_INT32_MAX : 0;
}
$resetTimestamp = min(max(0, $resetTimestamp * 1000), self::MONGO_INT32_MAX);
$update = ['$set' => ['resetTimestamp' => new \MongoDB\BSON\UTCDateTime($resetTimestamp), 'running' => true]];
$options = ['sort' => ['priority' => 1, 'created' => 1]];
//ints overflow to floats, should be fine
$end = microtime(true) + $waitDurationInMillis / 1000.0;
$sleepTime = $pollDurationInMillis * 1000;
//ints overflow to floats and already checked $pollDurationInMillis was positive
if (!is_int($sleepTime)) {
//ignore since testing a giant sleep takes too long
//@codeCoverageIgnoreStart
$sleepTime = PHP_INT_MAX;
}
//@codeCoverageIgnoreEnd
while (true) {
$message = $this->collection->findOneAndUpdate($completeQuery, $update, $options);
//checking if _id exist because findAndModify doesnt seem to return null when it can't match the query on
//older mongo extension
if ($message !== null && array_key_exists('_id', $message)) {
// findOneAndUpdate does not correctly return result according to typeMap options so just refetch.
$message = $this->collection->findOne(['_id' => $message->_id]);
//id on left of union operator so a possible id in payload doesnt wipe it out the generated one
return ['id' => $message['_id']] + (array) $message['payload'];
}
if (microtime(true) >= $end) {
return null;
}
usleep($sleepTime);
}
//ignore since always return from the function from the while loop
//@codeCoverageIgnoreStart
}