mambax7 /
newbb
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php declare(strict_types=1); |
||||
| 2 | |||||
| 3 | namespace XoopsModules\Newbb; |
||||
| 4 | |||||
| 5 | /** |
||||
| 6 | * NewBB, the forum module for XOOPS project |
||||
| 7 | * |
||||
| 8 | * @copyright XOOPS Project (https://xoops.org) |
||||
| 9 | * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html) |
||||
| 10 | * @author Taiwen Jiang (phppp or D.J.) <[email protected]> |
||||
| 11 | * @since 4.00 |
||||
| 12 | */ |
||||
| 13 | \defined('NEWBB_FUNCTIONS_INI') || require \dirname(__DIR__) . '/include/functions.ini.php'; |
||||
| 14 | |||||
| 15 | \define('NEWBB_STATS_TYPE_TOPIC', 1); |
||||
| 16 | \define('NEWBB_STATS_TYPE_POST', 2); |
||||
| 17 | \define('NEWBB_STATS_TYPE_DIGEST', 3); |
||||
| 18 | \define('NEWBB_STATS_TYPE_VIEW', 4); |
||||
| 19 | |||||
| 20 | \define('NEWBB_STATS_PERIOD_TOTAL', 1); |
||||
| 21 | \define('NEWBB_STATS_PERIOD_DAY', 2); |
||||
| 22 | \define('NEWBB_STATS_PERIOD_WEEK', 3); |
||||
| 23 | \define('NEWBB_STATS_PERIOD_MONTH', 4); |
||||
| 24 | |||||
| 25 | /** |
||||
| 26 | * Stats for forum |
||||
| 27 | */ |
||||
| 28 | class StatsHandler |
||||
| 29 | { |
||||
| 30 | /** @var \XoopsMySQLDatabase|null $db */ |
||||
| 31 | public ?\XoopsMySQLDatabase $db; |
||||
| 32 | /** @var string $table */ |
||||
| 33 | public string $table; |
||||
| 34 | /** @var array $param */ |
||||
| 35 | public array $param = [ |
||||
| 36 | 'type' => ['topic', 'post', 'digest', 'view'], |
||||
| 37 | 'period' => ['total', 'day', 'week', 'month'], |
||||
| 38 | ]; |
||||
| 39 | |||||
| 40 | /** |
||||
| 41 | * @param \XoopsMySQLDatabase|null $db |
||||
| 42 | */ |
||||
| 43 | public function __construct(\XoopsMySQLDatabase $db = null) |
||||
| 44 | { |
||||
| 45 | $this->db = $db; |
||||
| 46 | //if (!$db || !($db instanceof \XoopsMySQLDatabase)) { |
||||
| 47 | // $this->db = $GLOBALS['xoopsDB']; |
||||
| 48 | //} |
||||
| 49 | $this->table = $this->db->prefix('newbb_stats'); |
||||
|
0 ignored issues
–
show
|
|||||
| 50 | } |
||||
| 51 | |||||
| 52 | /** |
||||
| 53 | * @param \XoopsMySQLDatabase|null $db |
||||
| 54 | * @return StatsHandler |
||||
| 55 | */ |
||||
| 56 | public static function getInstance(\XoopsMySQLDatabase $db = null): StatsHandler |
||||
| 57 | { |
||||
| 58 | static $instance; |
||||
| 59 | if (null === $instance) { |
||||
| 60 | $instance = new static($db); |
||||
| 61 | } |
||||
| 62 | |||||
| 63 | return $instance; |
||||
| 64 | } |
||||
| 65 | |||||
| 66 | /** |
||||
| 67 | * @param int|string $id |
||||
| 68 | * @param string $type |
||||
| 69 | * @param int $increment |
||||
| 70 | * @return bool |
||||
| 71 | */ |
||||
| 72 | public function update($id, string $type, int $increment = 1): bool |
||||
| 73 | { |
||||
| 74 | $id = (int)$id; |
||||
| 75 | $increment = (int)$increment; |
||||
| 76 | |||||
| 77 | if ($increment !== 0 || false === ($type = \array_search($type, $this->param['type'], true))) { |
||||
| 78 | return false; |
||||
| 79 | } |
||||
| 80 | |||||
| 81 | $sql = " UPDATE {$this->table}" |
||||
| 82 | . ' SET stats_value = CASE ' |
||||
| 83 | . " WHEN time_format = '' OR DATE_FORMAT(time_update, time_format) = DATE_FORMAT(NOW(), time_format) THEN stats_value + '{$increment}' " |
||||
| 84 | . " ELSE '{$increment}' " |
||||
| 85 | . ' END, ' |
||||
| 86 | . ' time_update = NOW()' |
||||
| 87 | . ' WHERE ' |
||||
| 88 | . " (stats_id = '0' OR stats_id = '{$id}') " |
||||
| 89 | . " AND stats_type='{$type}' "; |
||||
| 90 | $result = $this->db->queryF($sql); |
||||
| 91 | $rows = $this->db->getAffectedRows(); |
||||
| 92 | if (0 == $rows) { |
||||
| 93 | $sql = " INSERT INTO {$this->table}" |
||||
| 94 | . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' |
||||
| 95 | . ' VALUES ' |
||||
| 96 | . " ('0', '{$increment}', '{$type}', '" |
||||
| 97 | . \array_search('total', $this->param['period'], true) |
||||
| 98 | . "', NOW(), ''), " |
||||
| 99 | . " ('0', '{$increment}', '{$type}', '" |
||||
| 100 | . \array_search('day', $this->param['period'], true) |
||||
| 101 | . "', NOW(), '%Y%j'), " |
||||
| 102 | . " ('0', '{$increment}', '{$type}', '" |
||||
| 103 | . \array_search('week', $this->param['period'], true) |
||||
| 104 | . "', NOW(), '%Y%u'), " |
||||
| 105 | . " ('0', '{$increment}', '{$type}', '" |
||||
| 106 | . \array_search('month', $this->param['period'], true) |
||||
| 107 | . "', NOW(), '%Y%m')"; |
||||
| 108 | $result = $this->db->queryF($sql); |
||||
| 109 | } |
||||
| 110 | if ($rows < 2 * (is_countable($this->param['period']) ? \count($this->param['period']) : 0) && !empty($id)) { |
||||
| 111 | $sql = " INSERT INTO {$this->table}" |
||||
| 112 | . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' |
||||
| 113 | . ' VALUES ' |
||||
| 114 | . " ('{$id}', '{$increment}', '{$type}', '" |
||||
| 115 | . \array_search('total', $this->param['period'], true) |
||||
| 116 | . "', NOW(), ''), " |
||||
| 117 | . " ('{$id}', '{$increment}', '{$type}', '" |
||||
| 118 | . \array_search('day', $this->param['period'], true) |
||||
| 119 | . "', NOW(), '%Y%j'), " |
||||
| 120 | . " ('{$id}', '{$increment}', '{$type}', '" |
||||
| 121 | . \array_search('week', $this->param['period'], true) |
||||
| 122 | . "', NOW(), '%Y%u'), " |
||||
| 123 | . " ('{$id}', '{$increment}', '{$type}', '" |
||||
| 124 | . \array_search('month', $this->param['period'], true) |
||||
| 125 | . "', NOW(), '%Y%m')"; |
||||
| 126 | $result = $this->db->queryF($sql); |
||||
| 127 | } |
||||
| 128 | return $result; |
||||
|
0 ignored issues
–
show
|
|||||
| 129 | } |
||||
| 130 | |||||
| 131 | /** |
||||
| 132 | * Get stats of "Today" |
||||
| 133 | * |
||||
| 134 | * @param array $ids ID of forum: > 0, forum; 0 - global; empty - all |
||||
| 135 | * @param array $types type of stats items: 1 - topic; 2 - post; 3 - digest; 4 - click; empty - all |
||||
| 136 | * @param array $periods time period: 1 - all time; 2 - today; 3 - this week; 4 - this month; empty - all |
||||
| 137 | * |
||||
| 138 | * @return array[][] |
||||
| 139 | * |
||||
| 140 | * @psalm-return array<string, array<array>> |
||||
| 141 | */ |
||||
| 142 | public function getStats(array $ids = [], array $types = [], array $periods = []): array |
||||
| 143 | { |
||||
| 144 | $ret = []; |
||||
| 145 | |||||
| 146 | $_types = []; |
||||
| 147 | foreach ($types as $type) { |
||||
| 148 | $_types[] = \array_search($type, $this->param['type'], true); |
||||
| 149 | } |
||||
| 150 | $_periods = []; |
||||
| 151 | foreach ($periods as $period) { |
||||
| 152 | $_periods[] = \array_search($period, $this->param['period'], true); |
||||
| 153 | } |
||||
| 154 | $sql = ' SELECT stats_id, stats_value, stats_type, stats_period ' . " FROM {$this->table} " . ' WHERE ' . " ( time_format = '' OR DATE_FORMAT(time_update, time_format) = DATE_FORMAT(NOW(), time_format) ) " . ' ' . ($ids === [] ? '' : 'AND stats_id IN (' . \implode( |
||||
| 155 | ', ', |
||||
| 156 | \array_map( |
||||
| 157 | '\intval', |
||||
| 158 | $ids |
||||
| 159 | ) |
||||
| 160 | ) . ')') . ' ' . ($_types === [] ? '' : 'AND stats_type IN (' . \implode(', ', $_types) . ')') . ' ' . ($_periods === [] ? '' : 'AND stats_period IN (' . \implode(', ', $_periods) . ')'); |
||||
| 161 | $result = $this->db->query($sql); |
||||
| 162 | if ($this->db->isResultSet($result)) { |
||||
| 163 | while (false !== ($row = $this->db->fetchArray($result))) { |
||||
|
0 ignored issues
–
show
It seems like
$result can also be of type boolean; however, parameter $result of XoopsMySQLDatabase::fetchArray() does only seem to accept mysqli_result, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 164 | $ret[(string)$row['stats_id']][$this->param['type'][$row['stats_type']]][$this->param['period'][$row['stats_period']]] = $row['stats_value']; |
||||
| 165 | } |
||||
| 166 | } |
||||
| 167 | return $ret; |
||||
| 168 | } |
||||
| 169 | |||||
| 170 | public function reset(): void |
||||
| 171 | { |
||||
| 172 | $this->db->queryF('TRUNCATE TABLE ' . $this->table); |
||||
| 173 | $now = \time(); |
||||
| 174 | $time_start = [ |
||||
| 175 | 'day' => '%Y%j', |
||||
| 176 | 'week' => '%Y%u', |
||||
| 177 | 'month' => '%Y%m', |
||||
| 178 | ]; |
||||
| 179 | $counts = []; |
||||
| 180 | |||||
| 181 | $sql = ' SELECT forum_id' . ' FROM ' . $this->db->prefix('newbb_forums'); |
||||
| 182 | $ret = $this->db->query($sql); |
||||
| 183 | if (!$this->db->isResultSet($ret)) { |
||||
| 184 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 185 | } |
||||
| 186 | while ([$forum_id] = $this->db->fetchRow($ret)) { |
||||
|
0 ignored issues
–
show
It seems like
$ret can also be of type boolean; however, parameter $result of XoopsMySQLDatabase::fetchRow() does only seem to accept mysqli_result, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 187 | $sql = ' SELECT COUNT(*), SUM(topic_views)' . ' FROM ' . $this->db->prefix('newbb_topics') . " WHERE approved=1 AND forum_id = {$forum_id}"; |
||||
| 188 | $result = $this->db->query($sql); |
||||
| 189 | if (!$this->db->isResultSet($result)) { |
||||
| 190 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 191 | } |
||||
| 192 | [$topics, $views] = $this->db->fetchRow($result); |
||||
| 193 | $this->update($forum_id, 'topic', (int)$topics); |
||||
| 194 | $this->update($forum_id, 'view', (int)$views); |
||||
| 195 | |||||
| 196 | $sql = ' SELECT COUNT(*)' . ' FROM ' . $this->db->prefix('newbb_topics') . " WHERE approved=1 AND topic_digest >0 AND forum_id = {$forum_id}"; |
||||
| 197 | $result = $this->db->query($sql); |
||||
| 198 | if (!$this->db->isResultSet($result)) { |
||||
| 199 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 200 | } |
||||
| 201 | [$digests] = $this->db->fetchRow($result); |
||||
| 202 | $this->update($forum_id, 'digest', (int)$digests); |
||||
| 203 | |||||
| 204 | $sql = ' SELECT COUNT(*)' . ' FROM ' . $this->db->prefix('newbb_posts') . " WHERE approved=1 AND forum_id = {$forum_id}"; |
||||
| 205 | $result = $this->db->query($sql); |
||||
| 206 | if (!$this->db->isResultSet($result)) { |
||||
| 207 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 208 | } |
||||
| 209 | [$posts] = $this->db->fetchRow($result); |
||||
| 210 | $this->update($forum_id, 'post', (int)$posts); |
||||
| 211 | |||||
| 212 | foreach ($time_start as $period => $format) { |
||||
| 213 | $sql = ' SELECT COUNT(*), SUM(topic_views)' . ' FROM ' . $this->db->prefix('newbb_topics') . " WHERE approved=1 AND forum_id = {$forum_id}" . " AND FROM_UNIXTIME(topic_time, '{$format}') >= FROM_UNIXTIME({$now}, '{$format}')"; |
||||
| 214 | $result = $this->db->query($sql); |
||||
| 215 | if (!$this->db->isResultSet($result)) { |
||||
| 216 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 217 | } |
||||
| 218 | [$topics, $views] = $this->db->fetchRow($result); |
||||
| 219 | $views = empty($views) ? 0 : $views; // null check |
||||
| 220 | |||||
| 221 | $sql = " INSERT INTO {$this->table}" . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' . ' VALUES ' . " ('{$forum_id}', '{$topics}', '" . \array_search('topic', $this->param['type'], true) . "', '" . \array_search( |
||||
| 222 | $period, |
||||
| 223 | $this->param['period'], |
||||
| 224 | true |
||||
| 225 | ) . "', NOW(), '{$format}')"; |
||||
| 226 | $result = $this->db->queryF($sql); |
||||
|
0 ignored issues
–
show
|
|||||
| 227 | |||||
| 228 | $sql = " INSERT INTO {$this->table}" . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' . ' VALUES ' . " ('{$forum_id}', '{$views}', '" . \array_search('view', $this->param['type'], true) . "', '" . \array_search( |
||||
| 229 | $period, |
||||
| 230 | $this->param['period'], |
||||
| 231 | true |
||||
| 232 | ) . "', NOW(), '{$format}')"; |
||||
| 233 | $result = $this->db->queryF($sql); |
||||
| 234 | @$counts['topic'][$period] += $topics; |
||||
| 235 | @$counts['view'][$period] += $views; |
||||
| 236 | |||||
| 237 | $sql = ' SELECT COUNT(*)' . ' FROM ' . $this->db->prefix('newbb_topics') . " WHERE approved=1 AND topic_digest >0 AND forum_id = {$forum_id}" . " AND FROM_UNIXTIME(digest_time, '{$format}') >= FROM_UNIXTIME({$now}, '{$format}')"; |
||||
| 238 | $result = $this->db->query($sql); |
||||
| 239 | if (!$this->db->isResultSet($result)) { |
||||
| 240 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 241 | } |
||||
| 242 | [$digests] = $this->db->fetchRow($result); |
||||
| 243 | $sql = " INSERT INTO {$this->table}" . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' . ' VALUES ' . " ('{$forum_id}', '{$digests}', '" . \array_search('digest', $this->param['type'], true) . "', '" . \array_search( |
||||
| 244 | $period, |
||||
| 245 | $this->param['period'], |
||||
| 246 | true |
||||
| 247 | ) . "', NOW(), '{$format}')"; |
||||
| 248 | $result = $this->db->queryF($sql); |
||||
| 249 | @$counts['digest'][$period] += $digests; |
||||
| 250 | |||||
| 251 | $sql = ' SELECT COUNT(*)' . ' FROM ' . $this->db->prefix('newbb_posts') . " WHERE approved=1 AND forum_id = {$forum_id}" . " AND FROM_UNIXTIME(post_time, '{$format}') >= FROM_UNIXTIME({$now}, '{$format}')"; |
||||
| 252 | $result = $this->db->query($sql); |
||||
| 253 | if (!$this->db->isResultSet($result)) { |
||||
| 254 | \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR); |
||||
| 255 | } |
||||
| 256 | [$posts] = $this->db->fetchRow($result); |
||||
| 257 | $sql = " INSERT INTO {$this->table}" . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' . ' VALUES ' . " ('{$forum_id}', '{$posts}', '" . \array_search('post', $this->param['type'], true) . "', '" . \array_search( |
||||
| 258 | $period, |
||||
| 259 | $this->param['period'], |
||||
| 260 | true |
||||
| 261 | ) . "', NOW(), '{$format}')"; |
||||
| 262 | $result = $this->db->queryF($sql); |
||||
| 263 | @$counts['post'][$period] += $posts; |
||||
| 264 | } |
||||
| 265 | } |
||||
| 266 | |||||
| 267 | $sql = " DELETE FROM {$this->table}" . " WHERE stats_id = '0' AND stats_period <> " . \array_search('total', $this->param['period'], true); |
||||
| 268 | $result = $this->db->queryF($sql); |
||||
| 269 | foreach ($time_start as $period => $format) { |
||||
| 270 | foreach (\array_keys($counts) as $type) { |
||||
| 271 | $sql = " INSERT INTO {$this->table}" . ' (`stats_id`, `stats_value`, `stats_type`, `stats_period`, `time_update`, `time_format`) ' . ' VALUES ' . " ('0', '{$counts[$type][$period]}', '" . \array_search($type, $this->param['type'], true) . "', '" . \array_search( |
||||
| 272 | $period, |
||||
| 273 | $this->param['period'], |
||||
| 274 | true |
||||
| 275 | ) . "', NOW(), '{$format}')"; |
||||
| 276 | $result = $this->db->queryF($sql); |
||||
| 277 | } |
||||
| 278 | } |
||||
| 279 | } |
||||
| 280 | } |
||||
| 281 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.