'emote_order', 'id' => 'emote_id', 'rank' => 'emote_hierarchy', ]; private IDbConnection $dbConn; private ?IDbStatement $getEmoteById = null; private ?IDbStatement $checkEmoteUrl = null; private ?IDbStatement $createEmote = null; private ?IDbStatement $deleteEmote = null; private ?IDbStatement $updateEmote = null; private ?IDbStatement $updateEmoteOrderOffset = null; private ?IDbStatement $getEmoteStrings = null; private ?IDbStatement $checkEmoteString = null; private ?IDbStatement $addEmoteString = null; private ?IDbStatement $removeEmoteString = null; public function __construct(IDbConnection $dbConn) { $this->dbConn = $dbConn; } public function getEmoteById(string $emoteId, bool $withStrings = false): EmoteInfo { if($this->getEmoteById === null) $this->getEmoteById = $this->dbConn->prepare( 'SELECT emote_id, emote_order, emote_hierarchy, emote_url FROM msz_emoticons WHERE emote_id = ?'); else $this->getEmoteById->reset(); $this->getEmoteById->addParameter(1, $emoteId); $this->getEmoteById->execute(); $result = $this->getEmoteById->getResult(); if(!$result->next()) throw new RuntimeException('No emoticon with ID exists.'); $strings = $withStrings ? $this->getEmoteStrings($emoteId) : []; return new EmoteInfo($result, $strings); } public static function emoteOrderOptions(): array { return array_keys(self::EMOTE_ORDER); } // TODO: pagination public function getAllEmotes( int $minRank = -1, string $orderBy = '', bool $desc = false, bool $withStrings = false ): array { if($minRank < 0) $minRank = PHP_INT_MAX; $orderBy = self::EMOTE_ORDER[$orderBy] ?? self::EMOTE_ORDER[array_key_first(self::EMOTE_ORDER)]; $getAll = $this->dbConn->prepare(sprintf( 'SELECT emote_id, emote_order, emote_hierarchy, emote_url FROM msz_emoticons WHERE emote_hierarchy <= ? ORDER BY %s %s', $orderBy, ($desc ? 'DESC' : 'ASC') )); $getAll->addParameter(1, $minRank); $getAll->execute(); $emotes = []; $result = $getAll->getResult(); if($withStrings) { while($result->next()) $emotes[] = new EmoteInfo($result, $this->getEmoteStrings((string)$result->getInteger(0))); } else { while($result->next()) $emotes[] = new EmoteInfo($result); } return $emotes; } 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; if($this->checkEmoteUrl === null) $this->checkEmoteUrl = $this->dbConn->prepare('SELECT COUNT(*) FROM msz_emoticons WHERE emote_url = ?'); else $this->checkEmoteUrl->reset(); $this->checkEmoteUrl->addParameter(1, $url); $this->checkEmoteUrl->execute(); $result = $this->checkEmoteUrl->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); if($this->createEmote === null) $this->createEmote = $this->dbConn->prepare('INSERT INTO msz_emoticons (emote_url, emote_hierarchy, emote_order) SELECT ?, ?, COALESCE(?, (SELECT FLOOR(MAX(emote_order) / 10) * 10 + 10 FROM msz_emoticons), 10)'); else $this->createEmote->reset(); $this->createEmote->addParameter(1, $url); $this->createEmote->addParameter(2, $minRank); $this->createEmote->addParameter(3, $order); $this->createEmote->execute(); return $this->getEmoteById((string)$this->dbConn->getLastInsertId()); } public function deleteEmote(EmoteInfo|string $infoOrId): void { if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); if($this->deleteEmote === null) $this->deleteEmote = $this->dbConn->prepare('DELETE FROM msz_emoticons WHERE emote_id = ?'); else $this->deleteEmote->reset(); $this->deleteEmote->addParameter(1, $infoOrId); $this->deleteEmote->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(); if($this->updateEmote === null) $this->updateEmote = $this->dbConn->prepare( 'UPDATE msz_emoticons SET emote_order = COALESCE(?, emote_order), emote_hierarchy = COALESCE(?, emote_hierarchy), emote_url = COALESCE(?, emote_url) WHERE emote_id = ?'); else $this->updateEmote->reset(); $this->updateEmote->addParameter(1, $order); $this->updateEmote->addParameter(2, $minRank); $this->updateEmote->addParameter(3, $url); $this->updateEmote->addParameter(4, $infoOrId); $this->updateEmote->execute(); } public function updateEmoteOrderOffset(EmoteInfo|string $infoOrId, int $offset): void { if($offset === 0) return; if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); if($this->updateEmoteOrderOffset === null) $this->updateEmoteOrderOffset = $this->dbConn->prepare('UPDATE msz_emoticons SET emote_order = emote_order + ? WHERE emote_id = ?'); else $this->updateEmoteOrderOffset->reset(); $this->updateEmoteOrderOffset->addParameter(1, $offset); $this->updateEmoteOrderOffset->addParameter(2, $infoOrId); $this->updateEmoteOrderOffset->execute(); } public function getEmoteStrings(EmoteInfo|string $infoOrId): array { if($infoOrId instanceof EmoteInfo) $infoOrId = $infoOrId->getId(); if($this->getEmoteStrings === null) $this->getEmoteStrings = $this->dbConn->prepare('SELECT emote_id, emote_string_order, emote_string FROM msz_emoticons_strings WHERE emote_id = ? ORDER BY emote_string_order'); else $this->getEmoteStrings->reset(); $this->getEmoteStrings->addParameter(1, $infoOrId); $this->getEmoteStrings->execute(); $strings = []; $result = $this->getEmoteStrings->getResult(); while($result->next()) $strings[] = new EmoteStringInfo($result); return $strings; } 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; if($this->checkEmoteString === null) $this->checkEmoteString = $this->dbConn->prepare('SELECT COUNT(*) FROM msz_emoticons_strings WHERE emote_string = ?'); else $this->checkEmoteString->reset(); $this->checkEmoteString->addParameter(1, $string); $this->checkEmoteString->execute(); $result = $this->checkEmoteString->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(); if($this->addEmoteString === null) $this->addEmoteString = $this->dbConn->prepare('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)'); else $this->addEmoteString->reset(); $this->addEmoteString->addParameter(1, $infoOrId); $this->addEmoteString->addParameter(2, $string); $this->addEmoteString->addParameter(3, $order); $this->addEmoteString->execute(); } public function removeEmoteString(EmoteStringInfo|string $infoOrString): void { if($infoOrString instanceof EmoteStringInfo) $infoOrString = $infoOrString->getString(); if($this->removeEmoteString === null) $this->removeEmoteString = $this->dbConn->prepare('DELETE FROM msz_emoticons_strings WHERE emote_string = ?'); else $this->removeEmoteString->reset(); $this->removeEmoteString->addParameter(1, $infoOrString); $this->removeEmoteString->execute(); } }