cache = new DbStatementCache($dbConn); } public function countMessages( UserInfo|string|null $ownerInfo = null, UserInfo|string|null $authorInfo = null, UserInfo|string|null $recipientInfo = null, MessageInfo|string|null $repliesFor = null, MessageInfo|string|null $replyTo = null, ?bool $sent = null, ?bool $read = null, ?bool $deleted = null ): int { $hasOwnerInfo = $ownerInfo !== null; $hasAuthorInfo = $authorInfo !== null; $hasRecipientInfo = $recipientInfo !== null; $hasRepliesFor = $repliesFor !== null; $hasReplyTo = $replyTo !== null; $hasSent = $sent !== null; $hasRead = $read !== null; $hasDeleted = $deleted !== null; $args = 0; $query = 'SELECT COUNT(*) FROM msz_messages'; if($hasOwnerInfo) { ++$args; $query .= ' WHERE msg_owner_id = ?'; } if($hasAuthorInfo) $query .= sprintf(' %s msg_author_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasRecipientInfo) $query .= sprintf(' %s msg_recipient_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasRepliesFor) $query .= sprintf(' %s msg_reply_to = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasReplyTo) { $query .= sprintf(' %s msg_id = ', ++$args > 1 ? 'AND' : 'WHERE'); if($replyTo instanceof MessageInfo) $query .= '?'; else $query .= '(SELECT reply_to FROM msz_messages WHERE msg_id = ?)'; } if($hasSent) $query .= sprintf(' %s msg_sent %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $sent ? 'IS NOT' : 'IS'); if($hasRead) $query .= sprintf(' %s msg_read %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $read ? 'IS NOT' : 'IS'); if($hasDeleted) $query .= sprintf(' %s msg_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS'); $query .= ' ORDER BY msg_created DESC'; $args = 0; $stmt = $this->cache->get($query); if($hasOwnerInfo) $stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); if($hasAuthorInfo) $stmt->addParameter(++$args, $authorInfo instanceof UserInfo ? $authorInfo->getId() : $authorInfo); if($hasRecipientInfo) $stmt->addParameter(++$args, $recipientInfo instanceof UserInfo ? $recipientInfo->getId() : $recipientInfo); if($hasRepliesFor) $stmt->addParameter(++$args, $repliesFor instanceof MessageInfo ? $repliesFor->getId() : $repliesFor); if($hasReplyTo) $stmt->addParameter(++$args, $replyTo instanceof MessageInfo ? $replyTo->getReplyToId() : $replyTo); $stmt->execute(); $result = $stmt->getResult(); return $result->next() ? $result->getInteger(0) : 0; } public function getMessages( UserInfo|string|null $ownerInfo = null, UserInfo|string|null $authorInfo = null, UserInfo|string|null $recipientInfo = null, MessageInfo|string|null $repliesFor = null, MessageInfo|string|null $replyTo = null, ?bool $sent = null, ?bool $read = null, ?bool $deleted = null, ?Pagination $pagination = null ): iterable { $hasOwnerInfo = $ownerInfo !== null; $hasAuthorInfo = $authorInfo !== null; $hasRecipientInfo = $recipientInfo !== null; $hasRepliesFor = $repliesFor !== null; $hasReplyTo = $replyTo !== null; $hasSent = $sent !== null; $hasRead = $read !== null; $hasDeleted = $deleted !== null; $hasPagination = $pagination !== null; $args = 0; $query = 'SELECT msg_id, msg_owner_id, msg_author_id, msg_recipient_id, msg_reply_to, msg_title, msg_body, msg_parser, UNIX_TIMESTAMP(msg_created), UNIX_TIMESTAMP(msg_sent), UNIX_TIMESTAMP(msg_read), UNIX_TIMESTAMP(msg_deleted) FROM msz_messages'; if($hasOwnerInfo) { ++$args; $query .= ' WHERE msg_owner_id = ?'; } if($hasAuthorInfo) $query .= sprintf(' %s msg_author_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasRecipientInfo) $query .= sprintf(' %s msg_recipient_id = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasRepliesFor) $query .= sprintf(' %s msg_reply_to = ?', ++$args > 1 ? 'AND' : 'WHERE'); if($hasReplyTo) { $query .= sprintf(' %s msg_id = ', ++$args > 1 ? 'AND' : 'WHERE'); if($replyTo instanceof MessageInfo) $query .= '?'; else $query .= '(SELECT reply_to FROM msz_messages WHERE msg_id = ?)'; } if($hasSent) $query .= sprintf(' %s msg_sent %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $sent ? 'IS NOT' : 'IS'); if($hasRead) $query .= sprintf(' %s msg_read %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $read ? 'IS NOT' : 'IS'); if($hasDeleted) $query .= sprintf(' %s msg_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS'); $query .= ' ORDER BY msg_created DESC'; if($hasPagination) $query .= ' LIMIT ? OFFSET ?'; $args = 0; $stmt = $this->cache->get($query); if($hasOwnerInfo) $stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); if($hasAuthorInfo) $stmt->addParameter(++$args, $authorInfo instanceof UserInfo ? $authorInfo->getId() : $authorInfo); if($hasRecipientInfo) $stmt->addParameter(++$args, $recipientInfo instanceof UserInfo ? $recipientInfo->getId() : $recipientInfo); if($hasRepliesFor) $stmt->addParameter(++$args, $repliesFor instanceof MessageInfo ? $repliesFor->getId() : $repliesFor); if($hasReplyTo) $stmt->addParameter(++$args, $replyTo instanceof MessageInfo ? $replyTo->getReplyToId() : $replyTo); if($hasPagination) { $stmt->addParameter(++$args, $pagination->getRange()); $stmt->addParameter(++$args, $pagination->getOffset()); } $stmt->execute(); return $stmt->getResult()->getIterator(MessageInfo::fromResult(...)); } public function getMessageInfo( UserInfo|string $ownerInfo, MessageInfo|string $messageInfoOrId, bool $useReplyTo = false ): MessageInfo { $stmt = $this->cache->get(sprintf( 'SELECT msg_id, msg_owner_id, msg_author_id, msg_recipient_id, msg_reply_to, msg_title, msg_body, msg_parser, UNIX_TIMESTAMP(msg_created), UNIX_TIMESTAMP(msg_sent), UNIX_TIMESTAMP(msg_read), UNIX_TIMESTAMP(msg_deleted) FROM msz_messages WHERE msg_id = %s AND msg_owner_id = ?', !$useReplyTo || $messageInfoOrId instanceof MessageInfo ? '?' : '(SELECT msg_reply_to FROM msz_messages WHERE msg_id = ?)' )); if($messageInfoOrId instanceof MessageInfo) $stmt->addParameter(1, $useReplyTo ? $messageInfoOrId->getReplyToId() : $messageInfoOrId->getId()); else $stmt->addParameter(1, $messageInfoOrId); $stmt->addParameter(2, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) throw new RuntimeException('Message not found.'); return MessageInfo::fromResult($result); } public function createMessage( string $messageId, UserInfo|string $ownerInfo, UserInfo|string|null $authorInfo, UserInfo|string|null $recipientInfo, string $title, string $body, int $parser, MessageInfo|string|null $replyTo = null, DateTime|int|null $sentAt = null, DateTime|int|null $readAt = null ): MessageInfo { $stmt = $this->cache->get('INSERT INTO msz_messages (msg_id, msg_owner_id, msg_author_id, msg_recipient_id, msg_reply_to, msg_title, msg_body, msg_parser, msg_sent, msg_read) VALUES (?, ?, ?, ?, ?, ?, ?, ?, FROM_UNIXTIME(?), FROM_UNIXTIME(?))'); $stmt->addParameter(1, $messageId); $stmt->addParameter(2, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); $stmt->addParameter(3, $authorInfo instanceof UserInfo ? $authorInfo->getId() : $authorInfo); $stmt->addParameter(4, $recipientInfo instanceof UserInfo ? $recipientInfo->getId() : $recipientInfo); $stmt->addParameter(5, $replyTo instanceof MessageInfo ? $replyTo->getId() : $replyTo); $stmt->addParameter(6, $title); $stmt->addParameter(7, $body); $stmt->addParameter(8, $parser); $stmt->addParameter(9, $sentAt instanceof DateTime ? $sentAt->getUnixTimeSeconds() : $sentAt); $stmt->addParameter(10, $readAt instanceof DateTime ? $readAt->getUnixTimeSeconds() : $readAt); $stmt->execute(); return $this->getMessageInfo($ownerInfo, $messageId); } public function updateMessage( UserInfo|string|null $ownerInfo = null, MessageInfo|string|null $messageInfo = null, ?string $title = null, ?string $body = null, ?int $parser = null, DateTime|int|null|false $sentAt = false, DateTime|int|null|false $readAt = false ): void { $setQuery = []; $setValues = []; $whereQuery = []; $whereValues = []; if($ownerInfo !== null) { $whereQuery[] = 'msg_owner_id = ?'; $whereValues[] = $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo; } if($messageInfo !== null) { $whereQuery[] = 'msg_id = ?'; $whereValues[] = $messageInfo instanceof MessageInfo ? $messageInfo->getId() : $messageInfo; } if($title !== null) { $setQuery[] = 'msg_title = ?'; $setValues[] = $title; } if($body !== null) { $setQuery[] = 'msg_body = ?'; $setValues[] = $body; } if($parser !== null) { $setQuery[] = 'msg_parser = ?'; $setValues[] = $parser; } if($sentAt !== false) { $setQuery[] = 'msg_sent = FROM_UNIXTIME(?)'; $setValues[] = $sentAt instanceof DateTime ? $sentAt->getUnixTimeSeconds() : $sentAt; } if($readAt !== false) { $setQuery[] = 'msg_read = FROM_UNIXTIME(?)'; $setValues[] = $readAt instanceof DateTime ? $readAt->getUnixTimeSeconds() : $readAt; } if(empty($whereQuery)) throw new InvalidArgumentException('$ownerInfo or $messageInfo must be specified.'); if(empty($setQuery)) return; $args = 0; $stmt = $this->cache->get(sprintf( 'UPDATE msz_messages SET %s WHERE %s', implode(', ', $setQuery), implode(' AND ', $whereQuery) )); foreach($setValues as $value) $stmt->addParameter(++$args, $value); foreach($whereValues as $value) $stmt->addParameter(++$args, $value); $stmt->execute(); } public function deleteMessages( UserInfo|string|null $ownerInfo, MessageInfo|array|string|null $messageInfos ): void { $hasOwnerInfo = $ownerInfo !== null; $hasMessageInfos = $messageInfos !== null; $query = 'UPDATE msz_messages SET msg_deleted = NOW() WHERE msg_deleted IS NULL'; if($hasOwnerInfo) $query .= ' AND msg_owner_id = ?'; if($hasMessageInfos) { if(!is_array($messageInfos)) $messageInfos = [$messageInfos]; $query .= sprintf( ' AND msg_id IN (%s)', DbTools::prepareListString($messageInfos) ); } $args = 0; $stmt = $this->cache->get($query); if($hasOwnerInfo) $stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); if($hasMessageInfos) foreach($messageInfos as $messageInfo) { if(is_string($messageInfo)) $stmt->addParameter(++$args, $messageInfo); elseif($messageInfo instanceof MessageInfo) $stmt->addParameter(++$args, $messageInfo->getId()); else throw new InvalidArgumentException('$messageInfos must be an array of strings or MessageInfo instances.'); } $stmt->execute(); } public function restoreMessages( UserInfo|string|null $ownerInfo, MessageInfo|array|string|null $messageInfos ): void { $hasOwnerInfo = $ownerInfo !== null; $hasMessageInfos = $messageInfos !== null; $query = 'UPDATE msz_messages SET msg_deleted = NULL WHERE msg_deleted IS NOT NULL'; if($hasOwnerInfo) $query .= ' AND msg_owner_id = ?'; if($hasMessageInfos) { if(!is_array($messageInfos)) $messageInfos = [$messageInfos]; $query .= sprintf( ' AND msg_id IN (%s)', DbTools::prepareListString($messageInfos) ); } $args = 0; $stmt = $this->cache->get($query); if($hasOwnerInfo) $stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); if($hasMessageInfos) foreach($messageInfos as $messageInfo) { if(is_string($messageInfo)) $stmt->addParameter(++$args, $messageInfo); elseif($messageInfo instanceof MessageInfo) $stmt->addParameter(++$args, $messageInfo->getId()); else throw new InvalidArgumentException('$messageInfos must be an array of strings or MessageInfo instances.'); } $stmt->execute(); } public function nukeMessages( UserInfo|string|null $ownerInfo, MessageInfo|array|string|null $messageInfos ): void { $hasOwnerInfo = $ownerInfo !== null; $hasMessageInfos = $messageInfos !== null; $query = 'DELETE FROM msz_messages WHERE msg_deleted IS NOT NULL'; if($hasOwnerInfo) $query .= ' AND msg_owner_id = ?'; if($hasMessageInfos) { if(!is_array($messageInfos)) $messageInfos = [$messageInfos]; $query .= sprintf( ' AND msg_id IN (%s)', DbTools::prepareListString($messageInfos) ); } $args = 0; $stmt = $this->cache->get($query); if($hasOwnerInfo) $stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo); if($hasMessageInfos) foreach($messageInfos as $messageInfo) { if(is_string($messageInfo)) $stmt->addParameter(++$args, $messageInfo); elseif($messageInfo instanceof MessageInfo) $stmt->addParameter(++$args, $messageInfo->getId()); else throw new InvalidArgumentException('$messageInfos must be an array of strings or MessageInfo instances.'); } $stmt->execute(); } }