Cleaned up various utility functions.

This commit is contained in:
flash 2023-07-05 01:33:12 +00:00
parent 1bb9bd6541
commit 027ed5aff6
13 changed files with 63 additions and 106 deletions

@ -1 +1 @@
Subproject commit bce5ba77a268ecd6338d0e3520e41ff4c40cbeda Subproject commit 8f908d7f688bd95fdedd868c5bf8d25a3b500b99

View file

@ -68,12 +68,16 @@ if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
); );
} }
if(isset($change) && !empty($_POST['tags']) && is_array($_POST['tags']) && array_test($_POST['tags'], 'ctype_digit')) { if(isset($change) && !empty($_POST['tags']) && is_array($_POST['tags'])) {
$applyTags = []; $applyTags = [];
foreach($_POST['tags'] as $tagId) foreach($_POST['tags'] as $tagId) {
if(!ctype_digit($tagId))
die('Invalid item encountered in roles list.');
try { try {
$applyTags[] = ChangelogTag::byId((int)filter_var($tagId, FILTER_SANITIZE_NUMBER_INT)); $applyTags[] = ChangelogTag::byId((int)filter_var($tagId, FILTER_SANITIZE_NUMBER_INT));
} catch(ChangelogTagNotFoundException $ex) {} } catch(ChangelogTagNotFoundException $ex) {}
}
$change->setTags($applyTags); $change->setTags($applyTags);
} }

View file

@ -61,16 +61,21 @@ if(CSRF::validateRequest() && $canEdit) {
} }
} }
if(!empty($_POST['roles']) && is_array($_POST['roles']) && array_test($_POST['roles'], 'ctype_digit')) { if(!empty($_POST['roles']) && is_array($_POST['roles'])) {
// Read user input array and throw intval on em
$applyRoles = [];
foreach($_POST['roles'] as $item) {
if(!ctype_digit($item))
die('Invalid item encountered in roles list.');
$applyRoles[] = (int)$item;
}
// Fetch existing roles // Fetch existing roles
$existingRoles = $userInfo->getRoles(); $existingRoles = $userInfo->getRoles();
// Initialise set array with existing roles // Initialise set array with existing roles
$setRoles = $existingRoles; $setRoles = $existingRoles;
// Read user input array and throw intval on em
$applyRoles = array_apply($_POST['roles'], 'intval');
// Storage array for roles to dump // Storage array for roles to dump
$removeRoles = []; $removeRoles = [];

View file

@ -50,12 +50,18 @@ if(!empty($_POST['warning']) && is_array($_POST['warning'])) {
if(!empty($customDuration)) { if(!empty($customDuration)) {
switch($warningDuration) { switch($warningDuration) {
case -100: // YYYY-MM-DD case -100: // YYYY-MM-DD
$splitDate = array_apply(explode('-', $customDuration, 3), function ($a) { $splitDate = explode('-', $customDuration, 3);
return (int)$a; if(count($splitDate) !== 3)
}); die('Invalid date specified.');
if(checkdate($splitDate[1], $splitDate[2], $splitDate[0])) $wYear = (int)$splitDate[0];
$warningDuration = mktime(0, 0, 0, $splitDate[1], $splitDate[2], $splitDate[0]) - time(); $wMonth = (int)$splitDate[1];
$wDay = (int)$splitDate[2];
if(checkdate($wMonth, $wDay, $wYear))
$warningDuration = mktime(0, 0, 0, $wMonth, $wDay, $wYear) - time();
else
die('Invalid date specified.');
break; break;
case -200: // Raw seconds case -200: // Raw seconds

View file

@ -173,17 +173,28 @@ class AuthToken {
return $token; return $token;
} }
public static function cookieDomain(bool $compatible = true): string {
$url = parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST);
if(empty($url))
$url = $_SERVER['HTTP_HOST'];
if(!filter_var($url, FILTER_VALIDATE_IP) && $compatible)
$url = '.' . $url;
return $url;
}
public function applyCookie(int $expires = 0): void { public function applyCookie(int $expires = 0): void {
if($expires > 0) if($expires > 0)
$this->cookieExpires = $expires; $this->cookieExpires = $expires;
else else
$expires = $this->cookieExpires; $expires = $this->cookieExpires;
setcookie('msz_auth', $this->pack(), $expires, '/', msz_cookie_domain(), !empty($_SERVER['HTTPS']), true); setcookie('msz_auth', $this->pack(), $expires, '/', self::cookieDomain(), !empty($_SERVER['HTTPS']), true);
} }
public static function nukeCookie(): void { public static function nukeCookie(): void {
setcookie('msz_auth', '', -9001, '/', msz_cookie_domain(), !empty($_SERVER['HTTPS']), true); setcookie('msz_auth', '', -9001, '/', self::cookieDomain(), !empty($_SERVER['HTTPS']), true);
setcookie('msz_auth', '', -9001, '/', '', !empty($_SERVER['HTTPS']), true); setcookie('msz_auth', '', -9001, '/', '', !empty($_SERVER['HTTPS']), true);
} }

View file

@ -87,7 +87,11 @@ function forum_perms_get_user(?int $forum, int $user): array {
$getPerms->bind('user_id_1', $user); $getPerms->bind('user_id_1', $user);
$getPerms->bind('user_id_2', $user); $getPerms->bind('user_id_2', $user);
return $memo[$memoId] = array_bit_or($perms, $getPerms->fetch()); $userPerms = $getPerms->fetch();
foreach($perms as $key => $value)
$perms[$key] |= $userPerms[$key] ?? 0;
return $memo[$memoId] = $perms;
} }
function forum_perms_get_role(?int $forum, int $role): array { function forum_perms_get_role(?int $forum, int $role): array {
@ -124,7 +128,11 @@ function forum_perms_get_role(?int $forum, int $role): array {
$getPerms->bind('forum_id', $forum); $getPerms->bind('forum_id', $forum);
$getPerms->bind('role_id', $role); $getPerms->bind('role_id', $role);
return $memo[$memoId] = array_bit_or($perms, $getPerms->fetch()); $userPerms = $getPerms->fetch();
foreach($perms as $key => $value)
$perms[$key] |= $userPerms[$key] ?? 0;
return $memo[$memoId] = $perms;
} }
function forum_perms_get_user_raw(?int $forum, int $user): array { function forum_perms_get_user_raw(?int $forum, int $user): array {

View file

@ -2,6 +2,7 @@
namespace Misuzu; namespace Misuzu;
use InvalidArgumentException; use InvalidArgumentException;
use Index\XArray;
// get rid of this garbage as soon as possible // get rid of this garbage as soon as possible
@ -16,7 +17,7 @@ class Memoizer {
} }
if(is_callable($find)) { if(is_callable($find)) {
$item = array_find($this->collection, $find) ?? $create(); $item = XArray::first($this->collection, $find) ?? $create();
if(method_exists($item, 'getId')) if(method_exists($item, 'getId'))
$this->collection[$item->getId()] = $item; $this->collection[$item->getId()] = $item;
else else

View file

@ -128,7 +128,9 @@ class NewsPost implements JsonSerializable {
return Parser::instance(Parser::MARKDOWN)->parseText($this->getText()); return Parser::instance(Parser::MARKDOWN)->parseText($this->getText());
} }
public function getFirstParagraph(): string { public function getFirstParagraph(): string {
return first_paragraph($this->getText()); $text = $this->getText();
$index = mb_strpos($text, "\n");
return $index === false ? $text : mb_substr($text, 0, $index);
} }
public function getParsedFirstParagraph(): string { public function getParsedFirstParagraph(): string {
return Parser::instance(Parser::MARKDOWN)->parseText($this->getFirstParagraph()); return Parser::instance(Parser::MARKDOWN)->parseText($this->getFirstParagraph());

View file

@ -23,7 +23,6 @@ final class TwigMisuzu extends AbstractExtension {
new TwigFilter('byte_symbol', 'byte_symbol'), new TwigFilter('byte_symbol', 'byte_symbol'),
new TwigFilter('parse_text', fn(string $text, int $parser): string => Parser::instance($parser)->parseText($text)), new TwigFilter('parse_text', fn(string $text, int $parser): string => Parser::instance($parser)->parseText($text)),
new TwigFilter('perms_check', 'perms_check'), new TwigFilter('perms_check', 'perms_check'),
new TwigFilter('clamp', 'clamp'),
new TwigFilter('time_diff', [$this, 'timeDiff'], ['needs_environment' => true]), new TwigFilter('time_diff', [$this, 'timeDiff'], ['needs_environment' => true]),
]; ];
} }

View file

@ -4,6 +4,7 @@ namespace Misuzu\Users;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
use JsonSerializable; use JsonSerializable;
use Index\XString;
use Index\Colour\Colour; use Index\Colour\Colour;
use Misuzu\DB; use Misuzu\DB;
use Misuzu\HasRankInterface; use Misuzu\HasRankInterface;
@ -666,7 +667,7 @@ class User implements HasRankInterface, JsonSerializable {
} }
public static function validatePassword(string $password): string { public static function validatePassword(string $password): string {
if(unique_chars($password) < self::PASSWORD_UNIQUE) if(XString::countUnique($password) < self::PASSWORD_UNIQUE)
return 'weak'; return 'weak';
return ''; return '';

View file

@ -55,9 +55,10 @@ final class Zalgo {
public static function getString(array $array, int $length): string { public static function getString(array $array, int $length): string {
$string = ''; $string = '';
$rngMax = count($array) - 1;
for($i = 0; $i < $length; $i++) for($i = 0; $i < $length; $i++)
$string .= array_rand_value($array); $string .= $array[mt_rand(0, $rngMax)];
return $string; return $string;
} }

View file

@ -322,7 +322,8 @@
{% if topic.topic_pages|default(0) > 1 %} {% if topic.topic_pages|default(0) > 1 %}
<div class="forum__topic__pagination"> <div class="forum__topic__pagination">
{% for i in 1..topic.topic_pages|clamp(0, 3) %} {% set topic_pages_start_end = min(3, topic.topic_pages) %}
{% for i in 1..topic_pages_start_end %}
<a href="{{ url('forum-topic', {'topic': topic.topic_id, 'page': i}) }}" class="forum__topic__pagination__item"> <a href="{{ url('forum-topic', {'topic': topic.topic_id, 'page': i}) }}" class="forum__topic__pagination__item">
{{ i }} {{ i }}
</a> </a>
@ -334,7 +335,8 @@
</div> </div>
{% endif %} {% endif %}
{% for i in (topic.topic_pages - 2)|clamp(4, topic.topic_pages)..topic.topic_pages %} {% set topic_pages_end_start = max(4, min(topic.topic_pages, topic.topic_pages - 2)) %}
{% for i in topic_pages_end_start..topic.topic_pages %}
<a href="{{ url('forum-topic', {'topic': topic.topic_id, 'page': i}) }}" class="forum__topic__pagination__item"> <a href="{{ url('forum-topic', {'topic': topic.topic_id, 'page': i}) }}" class="forum__topic__pagination__item">
{{ i }} {{ i }}
</a> </a>

View file

@ -1,62 +1,6 @@
<?php <?php
use Index\Colour\Colour; use Index\Colour\Colour;
function array_test(array $array, callable $func): bool {
foreach($array as $value)
if(!$func($value))
return false;
return true;
}
function array_apply(array $array, callable $func): array {
for($i = 0; $i < count($array); ++$i)
$array[$i] = $func($array[$i]);
return $array;
}
function array_bit_or(array $array1, array $array2): array {
foreach($array1 as $key => $value)
$array1[$key] |= $array2[$key] ?? 0;
return $array1;
}
function array_rand_value(array $array) {
return $array[mt_rand(0, count($array) - 1)];
}
function array_find(array $array, callable $callback) {
foreach($array as $item)
if($callback($item))
return $item;
return null;
}
function clamp($num, int $min, int $max): int {
return max($min, min($max, (int)$num));
}
function first_paragraph(string $text, string $delimiter = "\n"): string {
$index = mb_strpos($text, $delimiter);
return $index === false ? $text : mb_substr($text, 0, $index);
}
function unique_chars(string $input, bool $multibyte = true): int {
$chars = [];
$strlen = $multibyte ? 'mb_strlen' : 'strlen';
$substr = $multibyte ? 'mb_substr' : 'substr';
$length = $strlen($input);
for($i = 0; $i < $length; $i++) {
$current = $substr($input, $i, 1);
if(!in_array($current, $chars, true)) {
$chars[] = $current;
}
}
return count($chars);
}
function byte_symbol(int $bytes, bool $decimal = false, array $symbols = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']): string { function byte_symbol(int $bytes, bool $decimal = false, array $symbols = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']): string {
if($bytes < 1) if($bytes < 1)
return '0 B'; return '0 B';
@ -125,30 +69,3 @@ function html_colour(int|null|Colour $colour, $attribs = '--user-colour'): strin
return $css; return $css;
} }
function msz_server_timing(\Index\Performance\Timings $timings): string {
$laps = $timings->getLaps();
$timings = [];
foreach($laps as $lap) {
$timing = $lap->getName();
if($lap->hasComment())
$timing .= ';desc="' . strtr($lap->getComment(), ['"' => '\\"']) . '"';
$timing .= ';dur=' . round($lap->getDurationTime(), 5);
$timings[] = $timing;
}
return sprintf('Server-Timing: %s', implode(', ', $timings));
}
function msz_cookie_domain(bool $compatible = true): string {
$url = parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST);
if(empty($url))
$url = $_SERVER['HTTP_HOST'];
if(!filter_var($url, FILTER_VALIDATE_IP) && $compatible)
$url = '.' . $url;
return $url;
}