diff --git a/.travis.yml b/.travis.yml index 7282ffe..b2dc096 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,5 @@ install: - composer install --prefer-source script: + - vendor/bin/psalm - vendor/bin/phpunit diff --git a/composer.json b/composer.json index c26a252..cbc0133 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,17 @@ { - "name": "queue-interop/queue-interop", + "name": "zoon/queue-interop", "type": "library", - "description": "Promoting the interoperability of MQs objects. Based on Java JMS", + "description": "Promoting the interoperability of MQs objects. Based on Java JMS, modernized with psalm typehints.", "keywords": ["messaging", "queue", "jms", "mq", "message queue"], - "homepage": "https://github.com/queue-interop/queue-interop", + "homepage": "https://github.com/zoon/queue-interop", "license": "MIT", "require": { "php": "^7.1.3 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0", - "queue-interop/queue-spec": "^0.6@dev" + "queue-interop/queue-spec": "^0.6@dev", + "vimeo/psalm": "^4.7" }, "autoload": { "psr-4": { diff --git a/src/ConnectionFactory.php b/src/ConnectionFactory.php index 093b169..3a66b55 100644 --- a/src/ConnectionFactory.php +++ b/src/ConnectionFactory.php @@ -3,7 +3,13 @@ namespace Interop\Queue; +/** + * @psalm-mutable + */ interface ConnectionFactory { + /** + * @psalm-impure + */ public function createContext(): Context; } \ No newline at end of file diff --git a/src/Consumer.php b/src/Consumer.php index a7bf144..454ebcd 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -9,11 +9,13 @@ * to a message-consumer creation method supplied by a session. * * @see https://docs.oracle.com/javaee/7/api/javax/jms/MessageConsumer.html + * @psalm-mutable */ interface Consumer { /** * Gets the Queue associated with this queue receiver. + * @psalm-capabilities read-props */ public function getQueue(): Queue; @@ -23,21 +25,25 @@ public function getQueue(): Queue; * A timeout of zero never expires, and the call blocks indefinitely. * * Timeout is in milliseconds + * @psalm-impure */ public function receive(int $timeout = 0): ?Message; /** * Receives the next message if one is immediately available. + * @psalm-impure */ public function receiveNoWait(): ?Message; /** * Tell the MQ broker that the message was processed successfully. + * @psalm-capabilities read-props */ public function acknowledge(Message $message): void; /** * Tell the MQ broker that the message was rejected. + * @psalm-impure */ public function reject(Message $message, bool $requeue = false): void; } \ No newline at end of file diff --git a/src/Context.php b/src/Context.php index 6d341b4..00ff1b4 100644 --- a/src/Context.php +++ b/src/Context.php @@ -7,12 +7,30 @@ use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException; use Interop\Queue\Exception\TemporaryQueueNotSupportedException; +/** + * @psalm-mutable + */ interface Context { + /** + * Create message + * + * @param string $body + * @param array $properties + * @param array $headers + * @return Message + * @psalm-pure + */ public function createMessage(string $body = '', array $properties = [], array $headers = []): Message; + /** + * @psalm-pure + */ public function createTopic(string $topicName): Topic; + /** + * @psalm-pure + */ public function createQueue(string $queueName): Queue; /** @@ -21,22 +39,34 @@ public function createQueue(string $queueName): Queue; * It will be deleted once the connection is closed. * * @throws TemporaryQueueNotSupportedException + * @psalm-pure */ public function createTemporaryQueue(): Queue; + /** + * @psalm-impure + */ public function createProducer(): Producer; + /** + * @psalm-impure + */ public function createConsumer(Destination $destination): Consumer; /** * @throws SubscriptionConsumerNotSupportedException + * @psalm-pure */ public function createSubscriptionConsumer(): SubscriptionConsumer; /** * @throws PurgeQueueNotSupportedException + * @psalm-pure */ public function purgeQueue(Queue $queue): void; + /** + * @psalm-impure + */ public function close(): void; } \ No newline at end of file diff --git a/src/Destination.php b/src/Destination.php index cbd0fc2..0b45560 100644 --- a/src/Destination.php +++ b/src/Destination.php @@ -14,6 +14,7 @@ * it may contain provider-specific configuration information in addition to its address. * * @see https://docs.oracle.com/javaee/7/api/javax/jms/Destination.html + * @psalm-mutable */ interface Destination { diff --git a/src/Exception.php b/src/Exception.php index 687b475..1af3e75 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -3,6 +3,9 @@ namespace Interop\Queue; +/** + * @psalm-mutable + */ interface Exception extends \Throwable { } diff --git a/src/Exception/DeliveryDelayNotSupportedException.php b/src/Exception/DeliveryDelayNotSupportedException.php index 3b8310d..35b583b 100644 --- a/src/Exception/DeliveryDelayNotSupportedException.php +++ b/src/Exception/DeliveryDelayNotSupportedException.php @@ -10,8 +10,9 @@ class DeliveryDelayNotSupportedException extends Exception * @param \Throwable $previous * * @return static + * @psalm-pure */ - public static function providerDoestNotSupportIt(int $code = 0, \Throwable $previous = null): self + public static function providerDoestNotSupportIt(int $code = 0, ?\Throwable $previous = null): self { return new static('The provider does not support delivery delay feature', $code, $previous); } diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 265b34d..6e5929d 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -3,6 +3,16 @@ namespace Interop\Queue\Exception; +/** + * @psalm-consistent-constructor + */ class Exception extends \Exception implements \Interop\Queue\Exception { + /** + * @psalm-capabilities read-props + */ + public function __construct(string $message = "", int $code = 0, ?\Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + } } \ No newline at end of file diff --git a/src/Exception/InvalidDestinationException.php b/src/Exception/InvalidDestinationException.php index 50d50bd..638b524 100644 --- a/src/Exception/InvalidDestinationException.php +++ b/src/Exception/InvalidDestinationException.php @@ -9,7 +9,12 @@ class InvalidDestinationException extends Exception * @param mixed $destination * @param string $class * + * @template TClass + * @psalm-param class-string $class + * @psalm-assert TClass $destination + * * @throws static + * @psalm-pure */ public static function assertDestinationInstanceOf($destination, string $class): void { diff --git a/src/Exception/InvalidMessageException.php b/src/Exception/InvalidMessageException.php index 0373097..f7c911a 100644 --- a/src/Exception/InvalidMessageException.php +++ b/src/Exception/InvalidMessageException.php @@ -11,7 +11,12 @@ class InvalidMessageException extends Exception * @param Message $message * @param string $class * + * @template TClass as Message + * @psalm-param class-string $class + * @psalm-assert TClass $message + * * @throws static + * @psalm-pure */ public static function assertMessageInstanceOf(Message $message, string $class): void { diff --git a/src/Exception/PriorityNotSupportedException.php b/src/Exception/PriorityNotSupportedException.php index cb79721..459eb3b 100644 --- a/src/Exception/PriorityNotSupportedException.php +++ b/src/Exception/PriorityNotSupportedException.php @@ -10,8 +10,9 @@ class PriorityNotSupportedException extends Exception * @param \Throwable $previous * * @return static + * @psalm-pure */ - public static function providerDoestNotSupportIt(int $code = 0, \Throwable $previous = null): self + public static function providerDoestNotSupportIt(int $code = 0, ?\Throwable $previous = null): self { return new static('The provider does not support priority feature', $code, $previous); } diff --git a/src/Exception/PurgeQueueNotSupportedException.php b/src/Exception/PurgeQueueNotSupportedException.php index 0dd33e2..6ce6b8b 100644 --- a/src/Exception/PurgeQueueNotSupportedException.php +++ b/src/Exception/PurgeQueueNotSupportedException.php @@ -10,8 +10,9 @@ class PurgeQueueNotSupportedException extends Exception * @param \Throwable $previous * * @return static + * @psalm-pure */ - public static function providerDoestNotSupportIt(int $code = 0, \Throwable $previous = null): self + public static function providerDoestNotSupportIt(int $code = 0, ?\Throwable $previous = null): self { return new static('The provider does not support purge queue.', $code, $previous); } diff --git a/src/Exception/SubscriptionConsumerNotSupportedException.php b/src/Exception/SubscriptionConsumerNotSupportedException.php index b843b01..ed3d771 100644 --- a/src/Exception/SubscriptionConsumerNotSupportedException.php +++ b/src/Exception/SubscriptionConsumerNotSupportedException.php @@ -10,8 +10,9 @@ class SubscriptionConsumerNotSupportedException extends Exception * @param \Throwable $previous * * @return static + * @psalm-pure */ - public static function providerDoestNotSupportIt(int $code = 0, \Throwable $previous = null): self + public static function providerDoestNotSupportIt(int $code = 0, ?\Throwable $previous = null): self { return new static('The provider does not support subscription consumer.', $code, $previous); } diff --git a/src/Exception/TemporaryQueueNotSupportedException.php b/src/Exception/TemporaryQueueNotSupportedException.php index 04f65ad..695343a 100644 --- a/src/Exception/TemporaryQueueNotSupportedException.php +++ b/src/Exception/TemporaryQueueNotSupportedException.php @@ -10,8 +10,9 @@ class TemporaryQueueNotSupportedException extends Exception * @param \Throwable $previous * * @return static + * @psalm-pure */ - public static function providerDoestNotSupportIt(int $code = 0, \Throwable $previous = null): self + public static function providerDoestNotSupportIt(int $code = 0, ?\Throwable $previous = null): self { return new static('The provider does not support temporary queue feature', $code, $previous); } diff --git a/src/Exception/TimeToLiveNotSupportedException.php b/src/Exception/TimeToLiveNotSupportedException.php index b56b751..890ccb4 100644 --- a/src/Exception/TimeToLiveNotSupportedException.php +++ b/src/Exception/TimeToLiveNotSupportedException.php @@ -10,8 +10,9 @@ class TimeToLiveNotSupportedException extends Exception * @param \Throwable $previous * * @return static + * @psalm-pure */ - public static function providerDoestNotSupportIt(int $code = 0, \Throwable $previous = null): self + public static function providerDoestNotSupportIt(int $code = 0, ?\Throwable $previous = null): self { return new static('The provider does not support time to live feature', $code, $previous); } diff --git a/src/Impl/ConsumerVisibilityTimeoutTrait.php b/src/Impl/ConsumerVisibilityTimeoutTrait.php index d919f4f..06c5f62 100644 --- a/src/Impl/ConsumerVisibilityTimeoutTrait.php +++ b/src/Impl/ConsumerVisibilityTimeoutTrait.php @@ -23,7 +23,7 @@ public function getVisibilityTimeout(): ?int * The duration (in seconds) that the received messages are hidden from subsequent retrieve * requests after being retrieved by a ReceiveMessage request. */ - public function setVisibilityTimeout(int $visibilityTimeout = null): void + public function setVisibilityTimeout(?int $visibilityTimeout = null): void { $this->visibilityTimeout = $visibilityTimeout; } diff --git a/src/Impl/MessageTrait.php b/src/Impl/MessageTrait.php index ee014a9..d1b3906 100644 --- a/src/Impl/MessageTrait.php +++ b/src/Impl/MessageTrait.php @@ -97,7 +97,7 @@ public function isRedelivered(): bool return $this->redelivered; } - public function setCorrelationId(string $correlationId = null): void + public function setCorrelationId(?string $correlationId = null): void { $this->setHeader('correlation_id', $correlationId); } @@ -107,7 +107,7 @@ public function getCorrelationId(): ?string return $this->getHeader('correlation_id'); } - public function setMessageId(string $messageId = null): void + public function setMessageId(?string $messageId = null): void { $this->setHeader('message_id', $messageId); } @@ -124,12 +124,12 @@ public function getTimestamp(): ?int return null === $value ? null : (int) $value; } - public function setTimestamp(int $timestamp = null): void + public function setTimestamp(?int $timestamp = null): void { $this->setHeader('timestamp', $timestamp); } - public function setReplyTo(string $replyTo = null): void + public function setReplyTo(?string $replyTo = null): void { $this->setHeader('reply_to', $replyTo); } diff --git a/src/Message.php b/src/Message.php index 5433763..7b6f5b9 100644 --- a/src/Message.php +++ b/src/Message.php @@ -13,35 +13,105 @@ * Within this general form, the definition of a message varies significantly across products. * * @see https://docs.oracle.com/javaee/7/api/javax/jms/Message.html + * @psalm-mutable */ interface Message { + /** + * @psalm-capabilities read-props + */ public function getBody(): string; + /** + * @psalm-capabilities read-props|write-this-props|write-refs + */ public function setBody(string $body): void; + /** + * Set properties + * + * @param array $properties + * @return void + * @psalm-capabilities read-props|write-this-props|write-refs + */ public function setProperties(array $properties): void; /** * Returns [name => value, ...] + * @return array + * @psalm-capabilities read-props */ public function getProperties(): array; + /** + * Set property + * + * @param string $name + * @param mixed $value + * @return void + * @psalm-capabilities read-props|write-this-props|write-refs + */ public function setProperty(string $name, $value): void; + /** + * Get property + * + * @param string $name + * @param mixed $default + * + * @return mixed + * + * @template TDefault + * @psalm-param TDefault $default + * @psalm-return TDefault|mixed + * @psalm-capabilities read-props + */ public function getProperty(string $name, $default = null); + /** + * Set headers + * + * @param array $headers + * @return void + * @psalm-capabilities read-props|write-this-props|write-refs + */ public function setHeaders(array $headers): void; /** * Returns [name => value, ...] + * @return array + * @psalm-capabilities read-props */ public function getHeaders(): array; + /** + * Set header + * + * @param string $name + * @param mixed $value + * @return void + * @psalm-capabilities read-props|write-this-props|write-refs + */ public function setHeader(string $name, $value): void; + /** + * Get header + * + * @param string $name + * @param mixed $default + * + * @return mixed + * + * @template TDefault + * @psalm-param TDefault $default + * @psalm-return TDefault|mixed + * @psalm-capabilities read-props + */ public function getHeader(string $name, $default = null); + /** + * @psalm-capabilities read-props|write-this-props|write-refs + */ public function setRedelivered(bool $redelivered): void; /** @@ -49,6 +119,7 @@ public function setRedelivered(bool $redelivered): void; * The message is considered as redelivered, * when it was sent by a broker to consumer but consumer does not ACK or REJECT it. * The broker brings the message back to the queue and mark it as redelivered. + * @psalm-capabilities read-props */ public function isRedelivered(): bool; @@ -56,13 +127,15 @@ public function isRedelivered(): bool; * Sets the correlation ID for the message. * A client can use the correlation header field to link one message with another. * A typical use is to link a response message with its request message. + * @psalm-capabilities read-props|write-this-props|write-refs */ - public function setCorrelationId(string $correlationId = null): void; + public function setCorrelationId(?string $correlationId = null): void; /** * Gets the correlation ID for the message. * This method is used to return correlation ID values that are either provider-specific message IDs * or application-specific String values. + * @psalm-capabilities read-props */ public function getCorrelationId(): ?string; @@ -70,14 +143,16 @@ public function getCorrelationId(): ?string; * Sets the message ID. * Providers set this field when a message is sent. * This method can be used to change the value for a message that has been received. + * @psalm-capabilities read-props|write-this-props|write-refs */ - public function setMessageId(string $messageId = null): void; + public function setMessageId(?string $messageId = null): void; /** * Gets the message Id. * The MessageId header field contains a value that uniquely identifies each message sent by a provider. * * When a message is sent, MessageId can be ignored. + * @psalm-capabilities read-props */ public function getMessageId(): ?string; @@ -86,6 +161,7 @@ public function getMessageId(): ?string; * The timestamp header field contains the time a message was handed off to a provider to be sent. * It is not the time the message was actually transmitted, * because the actual send may occur later due to transactions or other client-side queueing of messages. + * @psalm-capabilities read-props */ public function getTimestamp(): ?int; @@ -93,8 +169,9 @@ public function getTimestamp(): ?int; * Sets the message timestamp. * Providers set this field when a message is sent. * This method can be used to change the value for a message that has been received. + * @psalm-capabilities read-props|write-this-props|write-refs */ - public function setTimestamp(int $timestamp = null): void; + public function setTimestamp(?int $timestamp = null): void; /** * Sets the destination to which a reply to this message should be sent. @@ -107,11 +184,13 @@ public function setTimestamp(int $timestamp = null): void; * In some cases a client may wish to match a request it sent earlier with a reply it has just received. * The client can use the CorrelationID header field for this purpose. + * @psalm-capabilities read-props|write-this-props|write-refs */ - public function setReplyTo(string $replyTo = null): void; + public function setReplyTo(?string $replyTo = null): void; /** * Gets the destination to which a reply to this message should be sent. + * @psalm-capabilities read-props */ public function getReplyTo(): ?string; } \ No newline at end of file diff --git a/src/Processor.php b/src/Processor.php index d26786e..6c8ecc6 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -3,6 +3,9 @@ namespace Interop\Queue; +/** + * @psalm-mutable + */ interface Processor { /** @@ -32,6 +35,7 @@ interface Processor * @param Context $context * * @return string|object with __toString method implemented + * @psalm-pure */ public function process(Message $message, Context $context); } \ No newline at end of file diff --git a/src/Producer.php b/src/Producer.php index 8cc0bab..7fef57e 100644 --- a/src/Producer.php +++ b/src/Producer.php @@ -9,12 +9,16 @@ use Interop\Queue\Exception\PriorityNotSupportedException; use Interop\Queue\Exception\TimeToLiveNotSupportedException; +/** + * @psalm-mutable + */ interface Producer { /** * @throws Exception if the provider fails to send the message due to some internal error * @throws InvalidDestinationException if a client uses this method with an invalid destination * @throws InvalidMessageException if an invalid message is specified + * @psalm-impure */ public function send(Destination $destination, Message $message): void; @@ -25,13 +29,15 @@ public function send(Destination $destination, Message $message): void; * The delivery delay is in milliseconds. Use null to unset delivery delay and use transport's mode * * @throws DeliveryDelayNotSupportedException if producer does not support delivery delay feature + * @psalm-capabilities read-props */ - public function setDeliveryDelay(int $deliveryDelay = null): self; + public function setDeliveryDelay(?int $deliveryDelay = null): self; /** * Gets the minimum length of time in milliseconds that must elapse after a message is sent before the provider may deliver the message to a consumer. * * @return int|null the delivery delay in milliseconds. + * @psalm-pure */ public function getDeliveryDelay(): ?int; @@ -44,11 +50,13 @@ public function getDeliveryDelay(): ?int; * Use null to unset priority and use transport's mode * * @throws PriorityNotSupportedException if producer does not support priority feature + * @psalm-capabilities read-props */ - public function setPriority(int $priority = null): self; + public function setPriority(?int $priority = null): self; /** * Return the priority of messages that are sent using this Producer + * @psalm-pure */ public function getPriority(): ?int; @@ -64,13 +72,15 @@ public function getPriority(): ?int; * The message time to live to be used, in milliseconds; a value of zero means that a message never expires. Use null to unset time to live and use transport's mode * * @throws TimeToLiveNotSupportedException if producer does not support time to live feature + * @psalm-capabilities read-props */ - public function setTimeToLive(int $timeToLive = null): self; + public function setTimeToLive(?int $timeToLive = null): self; /** * Returns the time to live of messages that are sent using this JMSProducer. * * @return int|null the message time to live in milliseconds; a value of zero means that a message never expires. + * @psalm-pure */ public function getTimeToLive(): ?int; } \ No newline at end of file diff --git a/src/Queue.php b/src/Queue.php index 2b876bf..59b6dec 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -3,10 +3,14 @@ namespace Interop\Queue; +/** + * @psalm-mutable + */ interface Queue extends Destination { /** * Gets the name of this queue. This is a destination one consumes messages from. + * @psalm-capabilities read-props */ public function getQueueName(): string; } diff --git a/src/SubscriptionConsumer.php b/src/SubscriptionConsumer.php index 10f28ef..0132d52 100644 --- a/src/SubscriptionConsumer.php +++ b/src/SubscriptionConsumer.php @@ -3,11 +3,15 @@ namespace Interop\Queue; +/** + * @psalm-mutable + */ interface SubscriptionConsumer { /** * The timeout is in milliseconds. * Set zero to consume endlessly or till a consumer returns false. + * @psalm-impure */ public function consume(int $timeout = 0): void; @@ -19,10 +23,17 @@ public function consume(int $timeout = 0): void; * and an optional second parameter the \Interop\Queue\Consumer from which the message was * consumed. The consumer will not return the processing thread back to * the PHP script until the callback function returns FALSE. + * @psalm-impure */ public function subscribe(Consumer $consumer, callable $callback): void; + /** + * @psalm-impure + */ public function unsubscribe(Consumer $consumer): void; + /** + * @psalm-impure + */ public function unsubscribeAll(): void; } \ No newline at end of file diff --git a/src/Topic.php b/src/Topic.php index 0bd38dd..d23b327 100644 --- a/src/Topic.php +++ b/src/Topic.php @@ -9,11 +9,13 @@ * For those methods that use a Destination as a parameter, a Topic object may used as an argument. * * @see https://docs.oracle.com/javaee/7/api/javax/jms/Topic.html + * @psalm-mutable */ interface Topic extends Destination { /** * Gets the name of this topic. This is a destination one sends messages to. + * @psalm-capabilities read-props */ public function getTopicName(): string; } \ No newline at end of file