/** * Get a non running message from the queue. * * @param array $query in same format as \MongoDB\Collection::find() where top level fields do not contain operators. * Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, * invalid {$and: [{...}, {...}]} * @param int $runningResetDuration second duration the message can stay unacked before it resets and can be * retreived again. * @param int $waitDurationInMillis millisecond duration to wait for a message. * @param int $pollDurationInMillis millisecond duration to wait between polls. * * @return array|null the message or null if one is not found * * @throws \InvalidArgumentException $runningResetDuration, $waitDurationInMillis or $pollDurationInMillis was not * an int * @throws \InvalidArgumentException key in $query was not a string */ 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 }
public function removeProperty($property) { CodeGuard::checkTypeAndThrow($property, 'string'); $filter = array($property => array('$exists' => true)); $updateCommand = array('$unset' => array($property => true)); $result = $this->_collection->updateMany($filter, $updateCommand); return $result->getModifiedCount(); }