* @copyright 2025-2026 Vateron Media * @link https://github.com/Vateron-Media/XC_VM * @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html */ class UserRepository { private static $db = null; /** * Inject the database handler (dependency injection). * * @param \XcVm\Core\Database\DatabaseHandler $db Database handler. * @return void */ public static function setDb($db): void { self::$db = $db; } /** * Get the injected database handler. * * @return object Database handler. * @throws \RuntimeException If setDb() was not called first. */ private static function db(): object { if (self::$db === null) { throw new \RuntimeException(static::class . '::setDb() must be called before use.'); } return self::$db; } /** * Fetch an admin/reseller user matching the given login credentials. * * @param string $rUsername Username. * @param string $rPassword Plain-text password. * @return array|null The user row, or null if credentials are invalid. */ public static function getAuthUserByCredentials($rUsername, $rPassword) { $db = self::db(); $db->query('SELECT `id`, `username`, `password`, `member_group_id`, `status` FROM `users` WHERE `username` = ? LIMIT 1;', $rUsername); if ($db->num_rows() == 1) { $rRow = $db->get_row(); if (Authenticator::checkPassword($rPassword, $rRow['password'])) { return $rRow; } } } /** * List resellers under a given owner. * * @param int $rOwner Owner user id. * @param bool $rIncludeSelf Include the owner in the result. * @return array Reseller rows. */ public static function getResellers($rOwner, $rIncludeSelf = true) { $db = self::db(); if ($rIncludeSelf) { $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ? OR `id` = ? ORDER BY `username` ASC;', $rOwner, $rOwner); } else { $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ? ORDER BY `username` ASC;', $rOwner); } return $db->get_rows(true, 'id'); } /** * Get the direct sub-users (reports) of the current user. * * @param array $rPermissions Effective permissions. * @param array $rUserInfo Current user row. * @param bool $rIncludeSelf Include the user themselves. * @return array Direct report rows. */ public static function getDirectReports($rPermissions, $rUserInfo, $rIncludeSelf = true) { $db = self::db(); $rUserIDs = $rPermissions['direct_reports']; if (!$rIncludeSelf) { } else { $rUserIDs[] = $rUserInfo['id']; } $rReturn = array(); if (0 >= count($rUserIDs)) { } else { $db->query('SELECT * FROM `users` WHERE `owner_id` IN (' . implode(',', array_map('intval', $rUserIDs)) . ') ORDER BY `username` ASC;'); if (0 >= $db->num_rows()) { } else { foreach ($db->get_rows() as $rRow) { $rReturn[intval($rRow['id'])] = $rRow; } } } return $rReturn; } /** * Resolve the parent of a user within the permission scope. * * @param array $rPermissions Effective permissions. * @param array $rUserInfo Current user row. * @param int $rID Target user id. * @return int Resolved parent user id. */ public static function getParent($rPermissions, $rUserInfo, $rID) { if (!isset($rPermissions['users'][$rID]['parent']) || $rPermissions['users'][$rID]['parent'] == 0 || $rPermissions['users'][$rID]['parent'] == $rUserInfo['id']) { return $rID; } return self::getParent($rPermissions, $rUserInfo, $rPermissions['users'][$rID]['parent']); } /** * Get all descendant sub-users of a user (full report tree). * * @param int $rUser User id. * @return array Sub-user rows keyed by id. */ public static function getSubUsers($rUser) { $db = self::db(); $rReturn = array(); $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ?;', $rUser); foreach ($db->get_rows() as $rRow) { $rReturn[$rRow['id']] = array('username' => $rRow['username'], 'parent' => $rUser); foreach (self::getSubUsers($rRow['id']) as $rUserID => $rUserData) { $rReturn[$rUserID] = $rUserData; } } return $rReturn; } /** * Fetch a line by id. * * @param int $rID Line id. * @return array|null The line row, or null if not found. */ public static function getLineById($rID) { $db = self::db(); $db->query('SELECT * FROM `lines` WHERE `id` = ?;', $rID); if ($db->num_rows() != 1) { } else { return $db->get_row(); } } /** * Fetch a registered (panel) user by id. * * @param int $rID User id. * @return array|null The user row, or null if not found. */ public static function getRegisteredUserById($rID) { $db = self::db(); $db->query('SELECT * FROM `users` WHERE `id` = ?;', $rID); if ($db->num_rows() != 1) { } else { return $db->get_row(); } } /** * List registered users under an owner. * * @param int|null $rOwner Owner id, or null for all. * @param bool $rIncludeSelf Include the owner in the result. * @return array Registered user rows. */ public static function getRegisteredUsers($rOwner = null, $rIncludeSelf = true) { $db = self::db(); $rReturn = array(); $db->query('SELECT * FROM `users` ORDER BY `username` ASC;'); if (0 >= $db->num_rows()) { } else { foreach ($db->get_rows() as $rRow) { if (!(!$rOwner || $rRow['owner_id'] == $rOwner || $rRow['id'] == $rOwner && $rIncludeSelf)) { } else { $rReturn[intval($rRow['id'])] = $rRow; } } } if (count($rReturn) == 0) { $rReturn[-1] = array(); } return $rReturn; } /** * Resolve a streaming user's info (auth + entitlements). * * Looks the user up by id or username/password, then assembles their * bouquets and, optionally, allowed channel ids and active connections. * * @param array $rSettings Panel settings. * @param bool $rCached Use cached lookups. * @param array $rBouquets Bouquet definitions. * @param int|null $rUserID User id (when known). * @param string|null $rUsername Username (credential lookup). * @param string|null $rPassword Password (credential lookup). * @param bool $rGetChannelIDs Include allowed channel ids. * @param bool $rGetConnections Include active connections. * @param string $rIP Client IP. * @return array|null User info, or null if not found. */ public static function getStreamingUserInfo($rSettings, $rCached, $rBouquets, $rUserID = null, $rUsername = null, $rPassword = null, $rGetChannelIDs = false, $rGetConnections = false, $rIP = '') { global $rBlockedISP, $rBlockedServers; $db = self::db(); $rUserInfo = null; if ($rCached) { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { if ($rSettings['case_sensitive_line']) { $rTokenPath = LINES_TMP_PATH . 'line_t_' . $rUsername; $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } else { $rTokenPath = LINES_TMP_PATH . 'line_t_' . strtolower($rUsername); $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } } else { if (!empty($rUsername) && !empty($rPassword)) { if ($rSettings['case_sensitive_line']) { $rCachePath = LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword; $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } else { $rCachePath = LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword); $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } } else { if (empty($rUserID)) { return false; } } } if ($rUserID) { $rInfoPath = LINES_TMP_PATH . 'line_i_' . $rUserID; if (file_exists($rInfoPath)) { $rUserInfo = igbinary_unserialize(file_get_contents($rInfoPath)); } else { return false; } } } else { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { $db->query('SELECT * FROM `lines` WHERE `is_mag` = 0 AND `is_e2` = 0 AND `access_token` = ? AND LENGTH(`access_token`) = 32', $rUsername); } else { if (!empty($rUsername) && !empty($rPassword)) { $db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `username` = ? AND `password` = ? LIMIT 1', $rUsername, $rPassword); } else { if (!empty($rUserID)) { $db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `id` = ?', $rUserID); } else { return false; } } } if (0 < $db->num_rows()) { $rUserInfo = $db->get_row(); } } if (!$rUserInfo) { return false; } if ($rCached) { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { if ($rUsername != $rUserInfo['access_token']) { return false; } } else { if (!empty($rUsername) && !empty($rPassword)) { if ($rUsername != $rUserInfo['username'] || $rPassword != $rUserInfo['password']) { return false; } } } } if ($rSettings['county_override_1st'] == 1 && empty($rUserInfo['forced_country']) && !empty($rIP) && $rUserInfo['max_connections'] == 1) { $rUserInfo['forced_country'] = GeoIPService::getIPInfo($rIP)['registered_country']['iso_code']; if ($rCached) { file_put_contents(SIGNALS_TMP_PATH . 'cache_' . md5('forced_country/' . $rUserInfo['id']), json_encode(array('forced_country/' . $rUserInfo['id'], $rUserInfo['forced_country']))); } else { $db->query('UPDATE `lines` SET `forced_country` = ? WHERE `id` = ?', $rUserInfo['forced_country'], $rUserInfo['id']); } } $allowedIPS = json_decode($rUserInfo['allowed_ips'], true); $allowedUa = json_decode($rUserInfo['allowed_ua'], true); $rUserInfo['bouquet'] = json_decode($rUserInfo['bouquet'], true); $rUserInfo['allowed_ips'] = array_filter(array_map('trim', is_array($allowedIPS) ? $allowedIPS : [])); $rUserInfo['allowed_ua'] = array_filter(array_map('trim', is_array($allowedUa) ? $allowedUa : [])); $rUserInfo['allowed_outputs'] = array_map('intval', json_decode($rUserInfo['allowed_outputs'], true)); $rUserInfo['output_formats'] = array(); if ($rCached) { foreach (igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'output_formats')) as $rRow) { if (in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { $rUserInfo['output_formats'][] = $rRow['output_key']; } } } else { $db->query('SELECT `access_output_id`, `output_key` FROM `output_formats`;'); foreach ($db->get_rows() as $rRow) { if (in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { $rUserInfo['output_formats'][] = $rRow['output_key']; } } } $rUserInfo['con_isp_name'] = null; $rUserInfo['isp_violate'] = 0; $rUserInfo['isp_is_server'] = 0; if ($rSettings['show_isps'] == 1 && !empty($rIP)) { $rISPLock = GeoIPService::getISP($rIP); if (is_array($rISPLock) && !empty($rISPLock['isp'])) { $rUserInfo['con_isp_name'] = $rISPLock['isp']; $rUserInfo['isp_asn'] = $rISPLock['autonomous_system_number']; $rUserInfo['isp_violate'] = BlocklistService::checkISP($rBlockedISP, $rUserInfo['con_isp_name']); if ($rSettings['block_svp'] == 1) { $rUserInfo['isp_is_server'] = intval(BlocklistService::checkServer($rBlockedServers, $rUserInfo['isp_asn'])); } } if (!empty($rUserInfo['con_isp_name']) && $rSettings['enable_isp_lock'] == 1 && $rUserInfo['is_stalker'] == 0 && $rUserInfo['is_isplock'] == 1 && !empty($rUserInfo['isp_desc']) && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc'])) { $rUserInfo['isp_violate'] = 1; } if ($rUserInfo['isp_violate'] == 0 && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc'])) { if ($rCached) { $rSignalKey = 'isp/' . $rUserInfo['id']; file_put_contents(SIGNALS_TMP_PATH . 'cache_' . md5($rSignalKey), json_encode(array($rSignalKey, json_encode(array($rUserInfo['con_isp_name'], $rUserInfo['isp_asn']))))); } else { $db->query('UPDATE `lines` SET `isp_desc` = ?, `as_number` = ? WHERE `id` = ?', $rUserInfo['con_isp_name'], $rUserInfo['isp_asn'], $rUserInfo['id']); } } } if ($rGetChannelIDs) { $rLiveIDs = $rVODIDs = $rRadioIDs = $rCategoryIDs = $rChannelIDs = $rSeriesIDs = array(); foreach ($rUserInfo['bouquet'] as $rID) { if (isset($rBouquets[$rID]['streams'])) { $rChannelIDs = array_merge($rChannelIDs, $rBouquets[$rID]['streams']); } if (isset($rBouquets[$rID]['series'])) { $rSeriesIDs = array_merge($rSeriesIDs, $rBouquets[$rID]['series']); } if (isset($rBouquets[$rID]['channels'])) { $rLiveIDs = array_merge($rLiveIDs, $rBouquets[$rID]['channels']); } if (isset($rBouquets[$rID]['movies'])) { $rVODIDs = array_merge($rVODIDs, $rBouquets[$rID]['movies']); } if (isset($rBouquets[$rID]['radios'])) { $rRadioIDs = array_merge($rRadioIDs, $rBouquets[$rID]['radios']); } } $rUserInfo['channel_ids'] = array_map('intval', array_unique($rChannelIDs)); $rUserInfo['series_ids'] = array_map('intval', array_unique($rSeriesIDs)); $rUserInfo['vod_ids'] = array_map('intval', array_unique($rVODIDs)); $rUserInfo['live_ids'] = array_map('intval', array_unique($rLiveIDs)); $rUserInfo['radio_ids'] = array_map('intval', array_unique($rRadioIDs)); } $rAllowedCategories = array(); $rCategoryMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'category_map')); foreach ($rUserInfo['bouquet'] as $rID) { $rAllowedCategories = array_merge($rAllowedCategories, ($rCategoryMap[$rID] ?: array())); } $rUserInfo['category_ids'] = array_values(array_unique($rAllowedCategories)); return $rUserInfo; } /** * Resolve user info for streaming endpoints (wrapper over getStreamingUserInfo). * * @param int|null $rUserID User id (when known). * @param string|null $rUsername Username (credential lookup). * @param string|null $rPassword Password (credential lookup). * @param bool $rGetChannelIDs Include allowed channel ids. * @param bool $rGetConnections Include active connections. * @param string $rIP Client IP. * @return array|null User info, or null if not found. */ public static function getUserInfo($rUserID = null, $rUsername = null, $rPassword = null, $rGetChannelIDs = false, $rGetConnections = false, $rIP = '') { global $rSettings; $db = self::db(); $rCached = $rSettings['enable_cache']; $rBouquets = BouquetService::getAll(); $rUserInfo = null; if ($rCached) { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { if ($rSettings['case_sensitive_line']) { $rTokenPath = LINES_TMP_PATH . 'line_t_' . $rUsername; $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } else { $rTokenPath = LINES_TMP_PATH . 'line_t_' . strtolower($rUsername); $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } } else { if (!empty($rUsername) && !empty($rPassword)) { if ($rSettings['case_sensitive_line']) { $rCachePath = LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword; $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } else { $rCachePath = LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword); $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } } else { if (!empty($rUserID)) { } else { return false; } } } if (!$rUserID) { } else { $rInfoPath = LINES_TMP_PATH . 'line_i_' . $rUserID; if (file_exists($rInfoPath)) { $rUserInfo = igbinary_unserialize(file_get_contents($rInfoPath)); } else { return false; } } } else { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { $db->query('SELECT * FROM `lines` WHERE `is_mag` = 0 AND `is_e2` = 0 AND `access_token` = ? AND LENGTH(`access_token`) = 32', $rUsername); } else { if (!empty($rUsername) && !empty($rPassword)) { $db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `username` = ? AND `password` = ? LIMIT 1', $rUsername, $rPassword); } else { if (!empty($rUserID)) { $db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `id` = ?', $rUserID); } else { return false; } } } if (0 >= $db->num_rows()) { } else { $rUserInfo = $db->get_row(); } } if (!$rUserInfo) { return false; } if (!$rCached) { } else { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { if ($rUsername == $rUserInfo['access_token']) { } else { return false; } } else { if (empty($rUsername) || empty($rPassword)) { } else { if (!($rUsername != $rUserInfo['username'] || $rPassword != $rUserInfo['password'])) { } else { return false; } } } } if (!($rSettings['county_override_1st'] == 1 && empty($rUserInfo['forced_country']) && !empty($rIP) && $rUserInfo['max_connections'] == 1)) { } else { $rUserInfo['forced_country'] = GeoIP::getCountry($rIP)['registered_country']['iso_code']; if ($rCached) { \RedisManager::setSignal('forced_country/' . $rUserInfo['id'], $rUserInfo['forced_country']); } else { $db->query('UPDATE `lines` SET `forced_country` = ? WHERE `id` = ?', $rUserInfo['forced_country'], $rUserInfo['id']); } } $allowedIPS = json_decode($rUserInfo['allowed_ips'], true); $allowedUa = json_decode($rUserInfo['allowed_ua'], true); $rUserInfo['bouquet'] = json_decode($rUserInfo['bouquet'], true); $rUserInfo['allowed_ips'] = array_filter(array_map('trim', is_array($allowedIPS) ? $allowedIPS : [])); $rUserInfo['allowed_ua'] = array_filter(array_map('trim', is_array($allowedUa) ? $allowedUa : [])); $rUserInfo['allowed_outputs'] = array_map('intval', json_decode($rUserInfo['allowed_outputs'], true)); $rUserInfo['output_formats'] = array(); if ($rCached) { foreach (igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'output_formats')) as $rRow) { if (!in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { } else { $rUserInfo['output_formats'][] = $rRow['output_key']; } } } else { $db->query('SELECT `access_output_id`, `output_key` FROM `output_formats`;'); foreach ($db->get_rows() as $rRow) { if (!in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { } else { $rUserInfo['output_formats'][] = $rRow['output_key']; } } } $rUserInfo['con_isp_name'] = null; $rUserInfo['isp_violate'] = 0; $rUserInfo['isp_is_server'] = 0; if ($rSettings['show_isps'] != 1 || empty($rIP)) { } else { $rISPLock = GeoIP::getISP($rIP); if (is_array($rISPLock)) { if (!empty($rISPLock['isp'])) { $rUserInfo['con_isp_name'] = $rISPLock['isp']; $rUserInfo['isp_asn'] = $rISPLock['autonomous_system_number']; $rUserInfo['isp_violate'] = GeoIP::isISPBlocked($rUserInfo['con_isp_name'], BlocklistService::getBlockedISP()); if ($rSettings['block_svp'] == 1) { $rUserInfo['isp_is_server'] = intval(GeoIP::isASNBlocked($rUserInfo['isp_asn'], BlocklistService::getBlockedServers())); } } } if (!(!empty($rUserInfo['con_isp_name']) && $rSettings['enable_isp_lock'] == 1 && $rUserInfo['is_stalker'] == 0 && $rUserInfo['is_isplock'] == 1 && !empty($rUserInfo['isp_desc']) && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc']))) { } else { $rUserInfo['isp_violate'] = 1; } if (!($rUserInfo['isp_violate'] == 0 && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc']))) { } else { if ($rCached) { \RedisManager::setSignal('isp/' . $rUserInfo['id'], json_encode(array($rUserInfo['con_isp_name'], $rUserInfo['isp_asn']))); } else { $db->query('UPDATE `lines` SET `isp_desc` = ?, `as_number` = ? WHERE `id` = ?', $rUserInfo['con_isp_name'], $rUserInfo['isp_asn'], $rUserInfo['id']); } } } if (!$rGetChannelIDs) { } else { $rLiveIDs = $rVODIDs = $rRadioIDs = $rCategoryIDs = $rChannelIDs = $rSeriesIDs = array(); foreach ($rUserInfo['bouquet'] as $rID) { if (!isset($rBouquets[$rID]['streams'])) { } else { $rChannelIDs = array_merge($rChannelIDs, $rBouquets[$rID]['streams']); } if (!isset($rBouquets[$rID]['series'])) { } else { $rSeriesIDs = array_merge($rSeriesIDs, $rBouquets[$rID]['series']); } if (!isset($rBouquets[$rID]['channels'])) { } else { $rLiveIDs = array_merge($rLiveIDs, $rBouquets[$rID]['channels']); } if (!isset($rBouquets[$rID]['movies'])) { } else { $rVODIDs = array_merge($rVODIDs, $rBouquets[$rID]['movies']); } if (!isset($rBouquets[$rID]['radios'])) { } else { $rRadioIDs = array_merge($rRadioIDs, $rBouquets[$rID]['radios']); } } $rUserInfo['channel_ids'] = array_map('intval', array_unique($rChannelIDs)); $rUserInfo['series_ids'] = array_map('intval', array_unique($rSeriesIDs)); $rUserInfo['vod_ids'] = array_map('intval', array_unique($rVODIDs)); $rUserInfo['live_ids'] = array_map('intval', array_unique($rLiveIDs)); $rUserInfo['radio_ids'] = array_map('intval', array_unique($rRadioIDs)); } $rAllowedCategories = array(); $rCategoryMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'category_map')); foreach ($rUserInfo['bouquet'] as $rID) { $rAllowedCategories = array_merge($rAllowedCategories, ($rCategoryMap[$rID] ?: array())); } $rUserInfo['category_ids'] = array_values(array_unique($rAllowedCategories)); return $rUserInfo; } /** * Resolve Enigma2 device user info. * * @param array $rDevice Device row (MAC etc.). * @param bool $rGetChannelIDs Include allowed channel ids. * @param bool $rGetBouquetInfo Include bouquet info. * @param bool $rGetConnections Include active connections. * @return array|null Device user info, or null if not found. */ public static function getE2Info($rDevice, $rGetChannelIDs = false, $rGetBouquetInfo = false, $rGetConnections = false) { $db = self::db(); if (empty($rDevice['device_id'])) { $db->query('SELECT * FROM `enigma2_devices` WHERE `mac` = ?', $rDevice['mac']); } else { $db->query('SELECT * FROM `enigma2_devices` WHERE `device_id` = ?', $rDevice['device_id']); } if (0 >= $db->num_rows()) { return false; } $rReturn = array(); $rReturn['enigma2'] = $db->get_row(); $rReturn['user_info'] = array(); if (!($rUserInfo = self::getUserInfo($rReturn['enigma2']['user_id'], null, null, $rGetChannelIDs, $rGetConnections))) { } else { $rReturn['user_info'] = $rUserInfo; } $rReturn['pair_line_info'] = array(); if (empty($rReturn['user_info'])) { } else { $rReturn['pair_line_info'] = array(); if (is_null($rReturn['user_info']['pair_id'])) { } else { if (!($rUserInfo = self::getUserInfo($rReturn['user_info']['pair_id'], null, null, $rGetChannelIDs, $rGetConnections))) { } else { $rReturn['pair_line_info'] = $rUserInfo; } } } return $rReturn; } }