Файловый менеджер - Редактировать - /var/www/html/mediawiki-1.43.1/extensions/LiveChat/includes/ChatData.php
Ðазад
<?php namespace LiveChat; use FormatJson; use InvalidArgumentException; use MediaWiki\MediaWikiServices; use User; use wAvatar; use Wikimedia\Rdbms\Database; use Wikimedia\Timestamp\ConvertibleTimestamp; use Workerman\Connection\ConnectionInterface; class ChatData { const TABLE = 'lch_messages'; const C_ID = 'lchm_id'; const C_PARENT = 'lchm_parent_id'; const C_ROOM_TYPE = 'lchm_room_type'; const C_ROOM_ID = 'lchm_room_id'; const C_USER_ID = 'lchm_user_id'; const C_USER_TEXT = 'lchm_user_text'; const C_MESSAGE = 'lchm_message'; const C_HAS_CHILDREN = 'lchm_has_children'; const C_TIMESTAMP = 'lchm_timestamp'; const TABLE_REACTION = 'lch_msg_reactions'; const C_REACTION_MESSAGE_ID = 'lchmr_msg_id'; const C_REACTION_USER_ID = 'lchmr_user_id'; const C_REACTION_USER_TEXT = 'lchmr_user_text'; const C_REACTION_TYPE = 'lchmr_type'; const C_REACTION_TIMESTAMP = 'lchmr_timestamp'; const CACHE_SIZE = 100; /** * @var array */ protected static $cache = []; const REACTIONS_BY_NAME = [ 'like' => 1, ]; const REACTIONS_BY_ID = [ 1 => 'like', ]; /** * @var Database */ private static $dbw; /** * @var Database */ private static $dbr; /** * @param ConnectionInterface $connection * @param string $name * @param string $command * @param array $data */ public static function onCommand( ConnectionInterface $connection, string $name, string $command, array $data ) { self::debugLog( __FUNCTION__, $name, $command, var_export( $data, true ) ); switch ( $command ) { case 'insert': self::onInsertCommand( $connection, $data ); break; case 'select': self::onSelectCommand( $connection, $data ); break; case 'update': self::onUpdateCommand( $connection, $data ); break; default: self::debugLog( __FUNCTION__, 'ERROR: Unknown command' ); } } /** * @param ConnectionInterface $connection * @param array $data */ protected static function onSelectCommand( ConnectionInterface $connection, array $data ) { $roomId = $data['roomId']; $roomType = $data['roomType']; $target = $data['target'] ?? null; $value = $data['message']['value'] ?? []; // $userId = $value['userId'] ?? 0; $userName = $value['userName'] ?? 'Undefined'; $fromId = $value['fromId'] ?? null; $parentId = $value['parentId'] ?? null; $roomCache = &self::getCache( [ $roomType, $roomId ] ); if ( !$roomCache ) { self::loadMessagesToCache( $roomType, $roomId ); } $reactions = &$roomCache['reactions']; $messagesCache = &$roomCache['messages']; $return = []; if ( $parentId ) { $parent = self::getParent( $roomType, $roomId, $parentId ); if ( $parent && !$fromId ) { // Add parent message if this is not a history request for disconnect events $return[] = $parent; } if ( $parent && isset( $roomCache['children'][$parentId] ) ) { $messagesCache = &$roomCache['children'][$parentId]; } else { $emptyArray = []; $messagesCache = &$emptyArray; } } if ( !$fromId || isset( $messagesCache[$fromId] ) ) { // TODO send reaction also when fromId provided foreach ( $messagesCache as $msgId => $message ) { // TODO optimize me if ( !$fromId || $msgId > $fromId ) { // Add user reaction $userReaction = $reactions[$msgId][$userName] ?? null; if ( $userReaction ) { $message['userReaction'] = $userReaction; } $return[] = $message; } } } else { // TODO load from database } $bufferData = [ 'event' => ChatRoom::EVENT_SEND_HISTORY, 'messages' => $return, ]; if ( $parentId ) { $bufferData['parentId'] = $parentId; } self::sendDataToManager( $connection, [ 'command' => 'send', 'message' => [ 'action' => Room::MANAGER_ACTION_SEND_TO_ALL, 'buffer' => FormatJson::encode( $bufferData ), ], 'key' => $data['key'] ?? null, 'roomId' => $roomId, 'roomType' => $roomType, ], $target ); } /** * Updates reactions * @param ConnectionInterface $connection * @param array $data */ protected static function onUpdateCommand( ConnectionInterface $connection, array $data ) { $target = $data['target'] ?? null; $message = $data['message'] ?? []; $value = $message['value'] ?? []; $id = $value['message'] ?? null; if ( !$id ) { self::debugLog( __FUNCTION__, 'ERROR: No message id provided' ); return; } $newReaction = $value['reaction'] ?? null; $newReactionId = self::REACTIONS_BY_NAME[$newReaction] ?? null; if ( !$newReactionId ) { self::debugLog( __FUNCTION__, "ERROR: Reaction $newReaction is not allowed" ); return; } $userId = $value['userId']; $userName = $value['userName']; $roomId = $data['roomId']; $roomType = $data['roomType']; $roomCache = &self::getCache( [ $roomType, $roomId ] ); if ( !$roomCache ) { self::loadMessagesToCache( $roomType, $roomId ); } $messagesCache = &$roomCache['messages']; $reactionsCache = &$roomCache['reactions']; $msgInCache = self::updateReaction( $messagesCache, $reactionsCache, $id, $userName, $newReaction ); if ( !$msgInCache ) { self::debugLog( __FUNCTION__, "ERROR: Message does not exist $roomType $roomId $id" ); return; } $messageReactions = $messagesCache[$id]['reactions']; $dbw = self::getDBW(); $time = Connection::getTime(); $timestamp = ConvertibleTimestamp::convert( TS_MW, $time ); $index = [ self::C_REACTION_MESSAGE_ID => $id, self::C_REACTION_USER_ID => $userId, self::C_REACTION_USER_TEXT => $userName, self::C_REACTION_TIMESTAMP => $timestamp, ]; $set = [ self::C_REACTION_TYPE => $newReactionId, ]; $dbw->upsert( self::TABLE_REACTION, [ $index + $set ], [ self::C_REACTION_MESSAGE_ID, self::C_REACTION_USER_TEXT ], $set, __METHOD__ ); $msg = [ 'event' => ChatRoom::EVENT_SEND_REACTION, 'id' => $id, 'reaction' => $newReaction, 'messageReactions' => $messageReactions, 'time' => $timestamp, ]; self::sendDataToManager( $connection, [ 'command' => 'send', 'message' => [ 'action' => Room::MANAGER_ACTION_SEND_TO_ALL, 'buffer' => FormatJson::encode( $msg ), ], 'key' => $data['key'] ?? null, 'roomId' => $roomId, 'roomType' => $roomType, ], $target ); } /** * @param array &$messages * @param array &$reactions */ protected static function loadReactionsForMessages( &$messages = [], &$reactions = [] ) { if ( !$messages ) { return; } $dbr = self::getDBR(); $vars = [ self::C_REACTION_MESSAGE_ID, self::C_REACTION_USER_ID, self::C_REACTION_USER_TEXT, self::C_REACTION_TYPE, ]; $cond = [ self::C_REACTION_MESSAGE_ID => array_keys( $messages ), ]; $res = $dbr->select( self::TABLE_REACTION, $vars, $cond, __METHOD__ ); foreach ( $res as $row ) { $a = (array)$row; $id = $a[self::C_REACTION_MESSAGE_ID]; $userText = $a[self::C_REACTION_USER_ID] ? User::newFromId( $a[self::C_REACTION_USER_ID] )->getName() : $a[self::C_REACTION_USER_TEXT]; $reactionName = self::REACTIONS_BY_ID[ $a[self::C_REACTION_TYPE] ] ?? 'undefined'; self::updateReaction( $messages, $reactions, $id, $userText, $reactionName ); } } /** * @param array &$messages * @param array &$reactions * @param string $id * @param string $userName * @param string $newReaction * @return bool */ private static function updateReaction( &$messages, &$reactions, $id, $userName, $newReaction ) { if ( empty( $messages[$id] ) ) { self::debugLog( __FUNCTION__, 'empty( $messages[$id] )', $id ); return false; } if ( !isset( $messages[$id]['reactions'] ) ) { self::debugLog( __FUNCTION__, 'empty( $messages[$id]["reactions"] )', $id ); $messages[$id]['reactions'] = []; } if ( !isset( $reactions[$id] ) ) { self::debugLog( __FUNCTION__, 'empty( $reactions[$id] )', $id ); $reactions[$id] = []; } $messageReactions =& $messages[$id]['reactions']; $oldReaction = $reactions[$id][$userName] ?? null; if ( $oldReaction ) { if ( $oldReaction === $newReaction ) { self::debugLog( __FUNCTION__, '$oldReaction === $newReaction', $id, $userName, $newReaction ); return true; } self::debugLog( __FUNCTION__, '$oldReaction', $oldReaction, '$newReaction', $newReaction ); if ( ( $messageReactions[$oldReaction] ?? 0 ) > 0 ) { $messageReactions[$oldReaction]--; // echo '$messageReactions[ $oldReaction ]--', "\n"; } } if ( empty( $messageReactions[$newReaction] ) ) { $messageReactions[$newReaction] = 1; // echo '$messageReactions[$newReaction] = 1;', "\n"; } else { $messageReactions[$newReaction]++; // echo '$messageReactions[$newReaction]++;', "\n"; } $reactions[$id][$userName] = $newReaction; return true; } /** * @param ConnectionInterface $connection * @param array $data */ protected static function onInsertCommand( ConnectionInterface $connection, array $data ) { $roomId = $data['roomId']; $roomType = $data['roomType']; $target = $data['target'] ?? null; $roomCache = &self::getCache( [ $roomType, $roomId ] ); $messagesCache = &$roomCache[ 'messages' ]; $value = $data['message']['value'] ?? []; $parentId = $value['parentId'] ?? null; $userId = $value['userId'] ?? 0; $userName = $value['userName'] ?? 'Undefined'; if ( $parentId ) { $parent = &self::getParent( $roomType, $roomId, $parentId, true ); if ( !$parent ) { $parentId = null; } } $text = $value['message'] ?? ''; if ( !$text ) { self::debugLog( __FUNCTION__, 'ERROR: Message is empty' ); } // Save to the database $time = Connection::getTime(); $row = [ self::C_ROOM_TYPE => $roomType, self::C_ROOM_ID => $roomId, self::C_PARENT => $parentId, self::C_USER_ID => $userId, self::C_USER_TEXT => $userName, self::C_MESSAGE => $text, self::C_HAS_CHILDREN => false, self::C_TIMESTAMP => ConvertibleTimestamp::convert( TS_MW, $time ), ]; $dbw = self::getDBW(); $dbw->insert( self::TABLE, $row, __METHOD__ ); $id = $dbw->insertId(); // Make Message $msg = self::makeMessageData( $roomType, $roomId, $id, $text, $userName, $userId, $time, $parentId, false ); if ( $id ) { if ( $parentId ) { $roomCache['children'][$parentId][$id] = &$messagesCache[$id]; if ( empty( $parent['hasChildren'] ) ) { $parent['hasChildren'] = 1; $dbw->update( self::TABLE, [ self::C_HAS_CHILDREN => true ], [ self::C_ID => $parentId ], __METHOD__ ); } } $messagesCache[$id] = $msg; // TODO need to develop more smarter cleaner (should work with parents and reactions) // if ( count( $messagesCache ) > static::CACHE_SIZE ) { // $min = min( array_keys( $messagesCache ) ); // unset( $messagesCache[$min] ); // } } else { self::debugLog( __FUNCTION__, 'ERROR: Cannot insert row' ); } self::sendDataToManager( $connection, [ 'command' => 'send', 'message' => [ 'action' => Room::MANAGER_ACTION_SEND_TO_ALL, 'buffer' => FormatJson::encode( [ 'event' => ChatRoom::EVENT_SEND_MESSAGE, 'messageData' => $msg, ] ), ], 'key' => $data['key'] ?? null, 'roomId' => $roomId, 'roomType' => $roomType, ], $target ); } /** * @param int $roomType * @param int $roomId * @param int $id * @param string|null $text * @param string $userName * @param int|null $userId * @param string $time * @param int|null $parentId * @param bool $hasChildren * @return array */ protected static function makeMessageData( int $roomType, int $roomId, $id, $text, $userName, $userId, $time, $parentId, $hasChildren ) { $msg = [ 'id' => $id, 'message' => MessageParser::parse( $text ), 'userName' => $userName, 'timestamp' => ConvertibleTimestamp::convert( TS_MW, $time ), ]; if ( $hasChildren ) { $msg['hasChildren'] = 1; } if ( $userId ) { $msg['userId'] = $userId; } if ( $parentId ) { $parent = self::getParent( $roomType, $roomId, $parentId, false ); if ( $parent ) { $msg['parentId'] = $parentId; $msg['parentUserName'] = $parent['userName']; } } $userAvatar = self::getUserAvatar( $userId ); if ( $userAvatar ) { $msg['userAvatar'] = $userAvatar; } return $msg; } /** * @param int|null $userId * @param string $size * @return string|null */ public static function getUserAvatar( $userId, $size = 'm' ) { if ( !$userId ) { return null; } $avatar = new wAvatar( $userId, $size ); if ( $avatar->isDefault() ) { return null; } global $wgUploadBaseUrl, $wgUploadPath; $avatarImg = $avatar->getAvatarImage(); return "{$wgUploadBaseUrl}{$wgUploadPath}/avatars/{$avatarImg}"; } /** * @param int $roomType * @param int $roomId * @param int $messageId * @param bool $withChildren * @return array */ protected static function loadMessage( int $roomType, int $roomId, int $messageId, bool $withChildren = false ) { $dbr = self::getDBR(); $conds = [ self::C_ROOM_TYPE => $roomType, self::C_ROOM_ID => $roomId, ]; if ( $withChildren ) { $ids = $dbr->makeList( [ self::C_ID => $messageId, self::C_PARENT => $messageId, ], LIST_OR ); $conds[] = $ids; } else { $conds[self::C_ID] = $messageId; } // TODO get user name from user table $res = $dbr->select( self::TABLE, '*', $conds, __METHOD__ ); if ( !$res ) { return []; } $parent = []; $children = []; foreach ( $res as $row ) { $msg = self::messageDataFromRow( $roomType, $roomId, (array)$row ); $id = $msg['id']; if ( $id == $messageId ) { $parent = $msg; } else { $children[$id] = $msg; } } if ( $children ) { $parentId = $parent['id']; $roomCache['children'][$parentId] = $children; } if ( $parent[self::C_ROOM_TYPE] != $roomType || $parent[self::C_ROOM_ID] != $roomId ) { return []; } return $parent; } /** * @param int $roomType * @param int $roomId * @param array $row * @return array */ protected static function messageDataFromRow( int $roomType, int $roomId, array $row ) { $msg = self::makeMessageData( $roomType, $roomId, $row[self::C_ID], $row[self::C_MESSAGE], $row[self::C_USER_TEXT], $row[self::C_USER_ID], $row[self::C_TIMESTAMP], $row[self::C_PARENT], $row[self::C_HAS_CHILDREN] ); return $msg; } /** * @param mixed ...$args */ protected static function debugLog( ...$args ) { wfDebugLog( __CLASS__, static::class . '::' . implode( '; ', $args ), false ); } /** * @param array $path * @return array */ protected static function &getCache( array $path ) { $ret = &self::$cache; foreach ( $path as $i => $k ) { if ( !isset( $ret[$k] ) ) { $ret[$k] = []; } if ( !is_array( $ret[$k] ) ) { $fail = implode( '.', array_slice( $path, 0, $i + 1 ) ); throw new InvalidArgumentException( "Path $fail is not an array" ); } $ret = &$ret[$k]; } return $ret; } /** * @return Database */ protected static function getDBW() { if ( !self::$dbw ) { self::$dbw = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_PRIMARY ); } return self::$dbw; } /** * @return Database */ protected static function getDBR() { if ( !self::$dbr ) { self::$dbr = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_REPLICA ); } return self::$dbr; } /** * @param ConnectionInterface $connection * @param array $data * @param array|null $target */ protected static function sendDataToManager( ConnectionInterface $connection, array $data, ?array $target = null ) { if ( $target ) { $data['message']['target'] = $target; } $buffer = FormatJson::encode( $data ); self::debugLog( __FUNCTION__, $buffer ); $connection->send( $buffer ); } /** * @param int $roomType * @param int $roomId */ protected static function loadMessagesToCache( int $roomType, int $roomId ) { self::debugLog( __FUNCTION__, $roomType, $roomId ); $roomCache = &self::getCache( [ $roomType, $roomId ] ); // if ( !$roomCache ) { … $roomCache = [ 'messages' => [], 'parents' => [], 'children' => [], 'reactions' => [], ]; // … } $dbr = self::getDBR(); $conds = [ self::C_ROOM_TYPE => $roomType, self::C_ROOM_ID => $roomId, ]; $options = [ 'LIMIT' => static::CACHE_SIZE, 'ORDER BY' => self::C_ID, ]; // TODO get user name from user table $res = $dbr->select( self::TABLE, '*', $conds, __METHOD__, $options ); if ( !$res ) { return; } $messageCache = &$roomCache['messages']; foreach ( $res as $row ) { $a = (array)$row; $parentId = $a[self::C_PARENT]; $msg = self::messageDataFromRow( $roomType, $roomId, $a ); $id = $msg['id']; $messageCache[$id] = $msg; if ( $parentId ) { $parent = &self::getParent( $roomType, $roomId, $parentId, true ); if ( $parent ) { $roomCache['children'][$parentId][$id] = &$messageCache[$id]; } } } self::loadReactionsForMessages( $messageCache, $roomCache['reactions'] ); } /** * @param int $roomType * @param int $roomId * @param int $parentId * @param bool $withChildren * @return array|null */ protected static function &getParent( int $roomType, int $roomId, int $parentId, bool $withChildren = false ): ?array { $parentsCache = &self::getCache( [ $roomType, $roomId, 'parents' ] ); if ( empty( $parentsCache[$parentId] ) ) { $messagesCache = &self::getCache( [ $roomType, $roomId, 'messages' ] ); if ( empty( $messagesCache[$parentId] ) ) { $parent = self::loadMessage( $roomType, $roomId, $parentId, $withChildren ); } else { $parent = &$messagesCache[$parentId]; } if ( !$parent || isset( $parent['parentId'] ) ) { $parent = null; // don't allow wrong parent and parent of children } else { $parentsCache[$parentId] =& $parent; } } else { $parent = &$parentsCache[$parentId]; } return $parent; } }
| ver. 1.1 | |
.
| PHP 8.4.18 | Ð“ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ñтраницы: 0 |
proxy
|
phpinfo
|
ÐаÑтройка