'emote_order', 'id' => 'emote_id', 'rank' => 'emote_hierarchy', ]; private IDbConnection $dbConn; private DbStatementCache $cache; public function __construct(IDbConnection $dbConn) { $this->dbConn = $dbConn; $this->cache = new DbStatementCache($dbConn); } public function getEmote(string $emoteId): EmoteInfo { $stmt = $this->cache->get('SELECT emote_id, emote_order, emote_hierarchy, emote_url FROM msz_emoticons WHERE emote_id = ?'); $stmt->addParameter(1, $emoteId); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) throw new RuntimeException('No emoticon with that ID exists.'); return EmoteInfo::fromResult($result); } public static function emoteOrderOptions(): array { return array_keys(self::EMOTE_ORDER); } // TODO: pagination public function getEmotes( ?int $minRank = null, ?string $orderBy = null, ?bool $reverse = null ): iterable { $hasMinRank = $minRank !== null; $hasOrderBy = $orderBy !== null; $hasReverse = $reverse !== null; $query = 'SELECT emote_id, emote_order, emote_hierarchy, emote_url FROM msz_emoticons'; if($hasMinRank) $query .= ' WHERE emote_hierarchy <= ?'; if($hasOrderBy) { if(!array_key_exists($orderBy, self::EMOTE_ORDER)) throw new InvalidArgumentException('Invalid $orderBy specified.'); $query .= sprintf(' ORDER BY %s', self::EMOTE_ORDER[$orderBy]); if($hasReverse) $query .= $reverse ? ' DESC' : ' ASC'; } $stmt = $this->cache->get($query); if($hasMinRank) $stmt->addParameter(1, $minRank); $stmt->execute(); return $stmt->getResult()->getIterator(EmoteInfo::fromResult(...)); } private static function checkEmoteUrlInternal(string $url): string { // more checks? if(empty($url)) return 'empty'; if(trim($url) !== $url) return 'spaces'; return ''; } public function checkEmoteUrl(string $url): string { $check = self::checkEmoteUrlInternal($url); if($check !== '') return $check; $stmt = $this->cache->get('SELECT COUNT(*) FROM msz_emoticons WHERE emote_url = ?'); $stmt->addParameter(1, $url); $stmt->execute(); $result = $stmt->getResult(); if($result->next() && $result->getInteger(0) > 0) return 'used'; return ''; } public function createEmote(string $url, int $minRank = 0, ?int $order = null): EmoteInfo { $check = self::checkEmoteUrlInternal($url); if($check !== '') throw new InvalidArgumentException('$url is not correctly formatted: ' . $check); $stmt = $this->cache->get('INSERT INTO msz_emoticons (emote_url, emote_hierarchy, emote_order) SELECT ?, ?, COALESCE(?, (SELECT FLOOR(MAX(emote_order) / 10) * 10 + 10 FROM msz_emoticons), 10)'); $stmt->addParameter(1, $url); $stmt->addParameter(2, $minRank); $stmt->addParameter(3, $order); $stmt->execute(); return $this->getEmote((string)$this->dbConn->getLastInsertId()); } public function deleteEmote(EmoteInfo|string $infoOrId): void { if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); $stmt = $this->cache->get('DELETE FROM msz_emoticons WHERE emote_id = ?'); $stmt->addParameter(1, $infoOrId); $stmt->execute(); } public function updateEmote( EmoteInfo|string $infoOrId, ?int $order = null, ?int $minRank = null, ?string $url = null ): void { if($url !== null) { $check = self::checkEmoteUrlInternal($url); if($check !== '') throw new InvalidArgumentException('$url is not correctly formatted: ' . $check); } if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); $stmt = $this->cache->get('UPDATE msz_emoticons SET emote_order = COALESCE(?, emote_order), emote_hierarchy = COALESCE(?, emote_hierarchy), emote_url = COALESCE(?, emote_url) WHERE emote_id = ?'); $stmt->addParameter(1, $order); $stmt->addParameter(2, $minRank); $stmt->addParameter(3, $url); $stmt->addParameter(4, $infoOrId); $stmt->execute(); } public function updateEmoteOrderOffset(EmoteInfo|string $infoOrId, int $offset): void { if($offset === 0) return; if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); $stmt = $this->cache->get('UPDATE msz_emoticons SET emote_order = emote_order + ? WHERE emote_id = ?'); $stmt->addParameter(1, $offset); $stmt->addParameter(2, $infoOrId); $stmt->execute(); } public function getEmoteStrings(EmoteInfo|string $infoOrId): iterable { if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); $stmt = $this->cache->get('SELECT emote_id, emote_string_order, emote_string FROM msz_emoticons_strings WHERE emote_id = ? ORDER BY emote_string_order'); $stmt->addParameter(1, $infoOrId); $stmt->execute(); return $stmt->getResult()->getIterator(EmoteStringInfo::fromResult(...)); } private static function checkEmoteStringInternal(string $string): string { if(empty($string)) return 'empty'; if(trim($string) !== $string) return 'spaces'; if(strtolower($string) !== $string) return 'case'; if(!preg_match('#^[a-z][a-z0-9_-]*[a-z0-9]$#', $string)) return 'format'; return ''; } public function checkEmoteString(string $string): string { $check = self::checkEmoteStringInternal($string); if($check !== '') return $check; $stmt = $this->cache->get('SELECT COUNT(*) FROM msz_emoticons_strings WHERE emote_string = ?'); $stmt->addParameter(1, $string); $stmt->execute(); $result = $stmt->getResult(); if($result->next() && $result->getInteger(0) > 0) return 'used'; return ''; } public function addEmoteString(EmoteInfo|string $infoOrId, string $string, ?int $order = null): void { $check = self::checkEmoteStringInternal($string); if($check !== '') throw new InvalidArgumentException('$string is not correctly formatted: ' . $check); if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); $stmt = $this->cache->get('INSERT INTO msz_emoticons_strings (emote_id, emote_string, emote_string_order) SELECT ? AS target_emote_id, ?, COALESCE(?, (SELECT MAX(emote_string_order) + 1 FROM msz_emoticons_strings WHERE emote_id = target_emote_id), 1)'); $stmt->addParameter(1, $infoOrId); $stmt->addParameter(2, $string); $stmt->addParameter(3, $order); $stmt->execute(); } public function removeEmoteString(EmoteStringInfo|string $infoOrString): void { if($infoOrString instanceof EmoteStringInfo) $infoOrString = $infoOrString->getString(); $stmt = $this->cache->get('DELETE FROM msz_emoticons_strings WHERE emote_string = ?'); $stmt->addParameter(1, $infoOrString); $stmt->execute(); } }