Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
63.84% |
113 / 177 |
|
90.00% |
27 / 30 |
CRAP | |
0.00% |
0 / 1 |
| Util | |
63.84% |
113 / 177 |
|
90.00% |
27 / 30 |
482.47 | |
0.00% |
0 / 1 |
| getCookieDomain | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| setCookie | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
| clearCookie | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
2.00 | |||
| clearNumericCookie | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| clearRequiredNumericCookie | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| getCookie | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| generateRandomString | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
| getPercentButAvoid100UntilComplete | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
| encrypt | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| decrypt | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| extract | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| extractWithDefault | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
| getRatio | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| getPercent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| indexByID | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| isInGithubCI | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| isInTestEnvironment | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
| getMyAddress | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
| getInternalAddress | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| addSqlCondition | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
| addSqlOrCondition | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| boolString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| getHealthBasedOnLevel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| query | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| clampOptional | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| strOrNull | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| getGraphColor | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| getGraphGridColor | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| getValueGraphHeight | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| smallScoreTableRowColor | |
6.15% |
4 / 65 |
|
0.00% |
0 / 1 |
933.07 | |||
| 1 | <?php |
| 2 | |
| 3 | class Util |
| 4 | { |
| 5 | public static function getCookieDomain(): ?string |
| 6 | { |
| 7 | $host = preg_replace('/:\d+$/', '', $_SERVER['HTTP_HOST'] ?? ''); |
| 8 | return $host; |
| 9 | } |
| 10 | |
| 11 | public static function setCookie($name, $value) |
| 12 | { |
| 13 | setcookie($name, $value, [ |
| 14 | 'expires' => time() + 365 * 24 * 60 * 60, |
| 15 | 'path' => '/', |
| 16 | 'secure' => true, |
| 17 | 'httponly' => false, |
| 18 | 'samesite' => 'Lax' |
| 19 | ]); |
| 20 | // Also update $_COOKIE so the value is available in the current request |
| 21 | $_COOKIE[$name] = $value; |
| 22 | } |
| 23 | |
| 24 | /* @return The value of the cleared cookie */ |
| 25 | public static function clearCookie(string $name): ?string |
| 26 | { |
| 27 | $previous = $_COOKIE[$name] ?? null; |
| 28 | |
| 29 | setcookie($name, '', |
| 30 | [ |
| 31 | 'expires' => time() - 3600, |
| 32 | 'path' => '/', |
| 33 | 'secure' => true, |
| 34 | 'httponly' => false, |
| 35 | 'samesite' => 'Lax' |
| 36 | ]); |
| 37 | |
| 38 | unset($_COOKIE[$name]); |
| 39 | if ($previous == 'deleted') |
| 40 | return null; |
| 41 | return $previous; |
| 42 | } |
| 43 | |
| 44 | /* @return Int value of the cleared cookie, returns null if the cookeie isn't present. throws if it isn't numeric */ |
| 45 | public static function clearNumericCookie(string $name): ?int |
| 46 | { |
| 47 | $result = Util::clearCookie($name); |
| 48 | if (!$result) |
| 49 | return null; |
| 50 | if (!is_numeric($result)) |
| 51 | throw new Exception("Cookie " . $name . " should be numeric but has value '" . strval($result) . "'."); |
| 52 | return intval($result); |
| 53 | } |
| 54 | |
| 55 | /* @return Int value of the cleared cookie, throws excpetion when the cookie isn't present or isn't numeric */ |
| 56 | public static function clearRequiredNumericCookie(string $name): int |
| 57 | { |
| 58 | $result = Util::clearNumericCookie($name); |
| 59 | if (is_null($result)) |
| 60 | throw new Exception("Cookie " . $name . " is expected to be defined, but it isn't"); |
| 61 | return $result; |
| 62 | } |
| 63 | |
| 64 | public static function getCookie(string $name, $default = null) |
| 65 | { |
| 66 | return isset($_COOKIE[$name]) ? $_COOKIE[$name] : $default; |
| 67 | } |
| 68 | |
| 69 | public static function generateRandomString($length = 20) |
| 70 | { |
| 71 | $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
| 72 | $charactersLength = strlen($characters); |
| 73 | $randomString = ''; |
| 74 | for ($i = 0; $i < $length; $i++) |
| 75 | $randomString .= $characters[rand(0, $charactersLength - 1)]; |
| 76 | return $randomString; |
| 77 | } |
| 78 | |
| 79 | public static function getPercentButAvoid100UntilComplete(int $value, int $max): int |
| 80 | { |
| 81 | assert($value >= 0); |
| 82 | assert($max >= 0); |
| 83 | assert($value <= $max); |
| 84 | $result = (int) round(Util::getRatio($value, $max) * 100); |
| 85 | if ($result == 100 && $value < $max) |
| 86 | return 99; |
| 87 | return $result; |
| 88 | } |
| 89 | |
| 90 | private const SECRET_KEY = 'my_simple_secret_keyx'; |
| 91 | private const SECRET_IV = 'my_simple_secret_ivx'; |
| 92 | private const ENCRYPT_METHOD = 'AES-256-CBC'; |
| 93 | |
| 94 | public static function encrypt(string $str): string |
| 95 | { |
| 96 | $key = hash('sha256', self::SECRET_KEY); |
| 97 | $iv = substr(hash('sha256', self::SECRET_IV), 0, 16); |
| 98 | return base64_encode(openssl_encrypt($str, self::ENCRYPT_METHOD, $key, 0, $iv)); |
| 99 | } |
| 100 | |
| 101 | public static function decrypt(string $str): string |
| 102 | { |
| 103 | $key = hash('sha256', self::SECRET_KEY); |
| 104 | $iv = substr(hash('sha256', self::SECRET_IV), 0, 16); |
| 105 | return openssl_decrypt(base64_decode($str), self::ENCRYPT_METHOD, $key, 0, $iv); |
| 106 | } |
| 107 | |
| 108 | public static function extract(string $name, array &$inputArray) |
| 109 | { |
| 110 | $result = $inputArray[$name]; |
| 111 | unset($inputArray[$name]); |
| 112 | return $result; |
| 113 | } |
| 114 | |
| 115 | public static function extractWithDefault(string $name, array &$inputArray, $default) |
| 116 | { |
| 117 | $result = $inputArray[$name]; |
| 118 | unset($inputArray[$name]); |
| 119 | if (isset($result)) |
| 120 | return $result; |
| 121 | return $default; |
| 122 | } |
| 123 | |
| 124 | public static function getRatio(float|int $amount, float|int $max): float |
| 125 | { |
| 126 | if ($max == 0) |
| 127 | return 0; |
| 128 | return $amount / $max; |
| 129 | } |
| 130 | |
| 131 | public static function getPercent(float|int $amount, float|int $max): float |
| 132 | { |
| 133 | return self::getRatio($amount, $max) * 100; |
| 134 | } |
| 135 | |
| 136 | public static function indexByID($array, $prefix1, $prefix2) |
| 137 | { |
| 138 | $result = []; |
| 139 | foreach ($array as $value) |
| 140 | $result[$value[$prefix1]['id']] = $value[$prefix1][$prefix2]; |
| 141 | return $result; |
| 142 | } |
| 143 | |
| 144 | public static function isInGithubCI() |
| 145 | { |
| 146 | if ($testEnvironment = @$_SERVER['TEST_ENVIRONMENT']) |
| 147 | return $testEnvironment == 'github-ci'; |
| 148 | if ($host = @$_SERVER['HTTP_HOST']) |
| 149 | return str_contains($host, 'host.docker.internal'); |
| 150 | return false; |
| 151 | } |
| 152 | |
| 153 | public static function isInTestEnvironment(): bool |
| 154 | { |
| 155 | if (@$_SERVER['DDEV_PRIMARY_URL'] && str_contains($_SERVER['DDEV_PRIMARY_URL'], "tsumego.ddev.site")) |
| 156 | return true; |
| 157 | return Util::isInGithubCI(); |
| 158 | } |
| 159 | |
| 160 | public static function getMyAddress() |
| 161 | { |
| 162 | if (Util::isInGithubCI()) |
| 163 | return $_SERVER['TEST_APP_URL']; |
| 164 | if ($url = @$_SERVER['DDEV_PRIMARY_URL'] && $_SERVER['HTTP_X_FORWARDED_HOST']) |
| 165 | return 'https://' . $_SERVER['HTTP_X_FORWARDED_HOST']; |
| 166 | return "https://test.tsumego.ddev.site:33003"; |
| 167 | } |
| 168 | |
| 169 | public static function getInternalAddress() |
| 170 | { |
| 171 | if (Util::isInGithubCI()) |
| 172 | return 'https://host.docker.internal:8443'; |
| 173 | return 'http://localhost'; |
| 174 | } |
| 175 | |
| 176 | public static function addSqlCondition(&$existingCondition, $condition): void |
| 177 | { |
| 178 | if (empty($condition)) |
| 179 | return; |
| 180 | if (empty($existingCondition)) |
| 181 | { |
| 182 | $existingCondition = $condition; |
| 183 | return; |
| 184 | } |
| 185 | $existingCondition .= " AND " ; |
| 186 | if (str_contains($condition, " OR ")) |
| 187 | $existingCondition .= '(' . $condition . ')'; |
| 188 | else |
| 189 | $existingCondition .= $condition; |
| 190 | } |
| 191 | |
| 192 | public static function addSqlOrCondition(&$existingCondition, $condition): void |
| 193 | { |
| 194 | if (empty($existingCondition)) |
| 195 | { |
| 196 | $existingCondition = $condition; |
| 197 | return; |
| 198 | } |
| 199 | $existingCondition .= " OR " . $condition; |
| 200 | } |
| 201 | |
| 202 | public static function boolString($bool) |
| 203 | { |
| 204 | return $bool ? 'true' : 'false'; |
| 205 | } |
| 206 | |
| 207 | public static function getHealthBasedOnLevel(int $level): int |
| 208 | { |
| 209 | return intdiv($level, 5) + 10; |
| 210 | } |
| 211 | |
| 212 | public static function query($sql, $params = []) |
| 213 | { |
| 214 | /** @phpstan-ignore-next-line */ |
| 215 | $stmt = ClassRegistry::init('Tsumego')->getDataSource()->getConnection()->prepare($sql); |
| 216 | $stmt->execute($params); |
| 217 | return $stmt->fetchAll(PDO::FETCH_ASSOC); |
| 218 | } |
| 219 | |
| 220 | public static function clampOptional($value, $min, $max) |
| 221 | { |
| 222 | $result = $value; |
| 223 | if (!is_null($min)) |
| 224 | $result = max($min, $result); |
| 225 | if (!is_null($max)) |
| 226 | $result = min($max, $result); |
| 227 | return $result; |
| 228 | } |
| 229 | |
| 230 | public static function strOrNull($input): ?string |
| 231 | { |
| 232 | if (is_null($input)) |
| 233 | return null; |
| 234 | return strval($input); |
| 235 | } |
| 236 | |
| 237 | public static function getGraphColor(): string |
| 238 | { |
| 239 | return Auth::lightMode() == Auth::$LIGHT_MODE ? '#ddd' : '#3e3e3e'; |
| 240 | } |
| 241 | |
| 242 | public static function getGraphGridColor(): string |
| 243 | { |
| 244 | return Auth::lightMode() == Auth::$LIGHT_MODE ? '#000' : '#fff'; |
| 245 | } |
| 246 | |
| 247 | public static function getValueGraphHeight($input) |
| 248 | { |
| 249 | return 160 + count($input) * 25; |
| 250 | } |
| 251 | |
| 252 | public static function smallScoreTableRowColor(int $index): string |
| 253 | { |
| 254 | if ($index == 0) |
| 255 | return '#ffec85'; |
| 256 | if ($index == 1) |
| 257 | return '#939393'; |
| 258 | if ($index == 2) |
| 259 | return '#c28d47'; |
| 260 | if ($index == 3) |
| 261 | return '#85e35d'; |
| 262 | if ($index == 4) |
| 263 | return '#85e35d'; |
| 264 | if ($index == 5) |
| 265 | return '#85e35d'; |
| 266 | if ($index == 6) |
| 267 | return '#85e35d'; |
| 268 | if ($index == 7) |
| 269 | return '#85e35d'; |
| 270 | if ($index == 8) |
| 271 | return '#85e35d'; |
| 272 | if ($index == 9) |
| 273 | return '#85e35d'; |
| 274 | if ($index == 10) |
| 275 | return '#85e35d'; |
| 276 | if ($index == 11) |
| 277 | return '#85e35d'; |
| 278 | if ($index == 12) |
| 279 | return '#85e35d'; |
| 280 | if ($index == 13) |
| 281 | return '#85e35d'; |
| 282 | if ($index == 14) |
| 283 | return '#85e35d'; |
| 284 | if ($index == 15) |
| 285 | return '#85e35d'; |
| 286 | if ($index == 16) |
| 287 | return '#85e35d'; |
| 288 | if ($index == 17) |
| 289 | return '#85e35d'; |
| 290 | if ($index == 18) |
| 291 | return '#85e35d'; |
| 292 | if ($index == 19) |
| 293 | return '#85e35d'; |
| 294 | if ($index == 20) |
| 295 | return '#9cf974'; |
| 296 | if ($index == 21) |
| 297 | return '#9cf974'; |
| 298 | if ($index == 22) |
| 299 | return '#9cf974'; |
| 300 | if ($index == 23) |
| 301 | return '#9cf974'; |
| 302 | if ($index == 24) |
| 303 | return '#9cf974'; |
| 304 | if ($index == 25) |
| 305 | return '#9cf974'; |
| 306 | if ($index == 26) |
| 307 | return '#9cf974'; |
| 308 | if ($index == 27) |
| 309 | return '#9cf974'; |
| 310 | if ($index == 28) |
| 311 | return '#9cf974'; |
| 312 | if ($index == 29) |
| 313 | return '#9cf974'; |
| 314 | if ($index < 40) |
| 315 | return '#b6f998'; |
| 316 | if ($index < 50) |
| 317 | return '#d3f9c2'; |
| 318 | return '#e8f9e0'; |
| 319 | } |
| 320 | } |