Path part of URL. // [1] => Query part of URL. // [2] => Fragment part of URL. // // text surrounded by < and > will be replaced accordingly to an array of variables supplied to the format function // text surrounded by [ and ] will be replaced by the constant/define of that name // text surrounded by { and } will be replaced by a CSRF token with the given text as its realm, this will have no effect in a sessionless environment define('MSZ_URLS', [ 'index' => ['/'], 'info' => ['/info/'], 'search-index' => ['/search.php'], 'search-query' => ['/search.php', ['q' => '<query>']], 'auth-login' => ['/auth/login.php', ['username' => '<username>', 'redirect' => '<redirect>']], 'auth-login-welcome' => ['/auth/login.php', ['welcome' => '1', 'username' => '<username>']], 'auth-register' => ['/auth/register.php'], 'auth-forgot' => ['/auth/password.php'], 'auth-reset' => ['/auth/password.php', ['user' => '<user>']], 'auth-logout' => ['/auth/logout.php', ['csrf' => '{csrf}']], 'auth-resolve-user' => ['/auth/login.php', ['resolve' => '1', 'name' => '<username>']], 'auth-two-factor' => ['/auth/twofactor.php', ['token' => '<token>']], 'changelog-index' => ['/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>']], 'changelog-feed-rss' => ['/changelog.rss'], 'changelog-feed-atom' => ['/changelog.atom'], 'changelog-change' => ['/changelog/change/<change>'], 'changelog-change-comments' => ['/changelog/change/<change>', [], 'comments'], 'news-index' => ['/news', ['p' => '<page>']], 'news-category' => ['/news/<category>', ['p' => '<page>']], 'news-post' => ['/news/post/<post>'], 'news-post-comments' => ['/news/post/<post>', [], 'comments'], 'news-feed-rss' => ['/news.rss'], 'news-category-feed-rss' => ['/news/<category>.rss'], 'news-feed-atom' => ['/news.atom'], 'news-category-feed-atom' => ['/news/<category>.atom'], 'forum-index' => ['/forum'], 'forum-leaderboard' => ['/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']], 'forum-mark-global' => ['/forum/mark-as-read'], 'forum-mark-single' => ['/forum/mark-as-read', ['forum' => '<forum>']], 'forum-topic-new' => ['/forum/posting.php', ['f' => '<forum>']], 'forum-reply-new' => ['/forum/posting.php', ['t' => '<topic>']], 'forum-category' => ['/forum/forum.php', ['f' => '<forum>', 'p' => '<page>']], 'forum-topic' => ['/forum/topic.php', ['t' => '<topic>', 'page' => '<page>']], 'forum-topic-create' => ['/forum/posting.php', ['f' => '<forum>']], 'forum-topic-bump' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'bump', 'csrf' => '{csrf}']], 'forum-topic-lock' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'lock', 'csrf' => '{csrf}']], 'forum-topic-unlock' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'unlock', 'csrf' => '{csrf}']], 'forum-topic-delete' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'delete', 'csrf' => '{csrf}']], 'forum-topic-restore' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'restore', 'csrf' => '{csrf}']], 'forum-topic-nuke' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'nuke', 'csrf' => '{csrf}']], 'forum-post' => ['/forum/topic.php', ['p' => '<post>'], '<post_fragment>'], 'forum-post-create' => ['/forum/posting.php', ['t' => '<topic>']], 'forum-post-delete' => ['/forum/post.php', ['p' => '<post>', 'm' => 'delete']], 'forum-post-restore' => ['/forum/post.php', ['p' => '<post>', 'm' => 'restore']], 'forum-post-nuke' => ['/forum/post.php', ['p' => '<post>', 'm' => 'nuke']], 'forum-post-quote' => ['/forum/posting.php', ['q' => '<post>']], 'forum-post-edit' => ['/forum/posting.php', ['p' => '<post>', 'm' => 'edit']], 'user-list' => ['/members.php', ['r' => '<role>', 'ss' => '<sort>', 'sd' => '<direction>', 'p' => '<page>']], 'user-profile' => ['/profile.php', ['u' => '<user>']], 'user-profile-forum-topics' => ['/profile.php', ['u' => '<user>', 'm' => 'forum-topics']], 'user-profile-forum-posts' => ['/profile.php', ['u' => '<user>', 'm' => 'forum-posts']], 'user-profile-edit' => ['/profile.php', ['u' => '<user>', 'edit' => '1']], 'user-account-standing' => ['/profile.php', ['u' => '<user>'], 'account-standing'], 'user-avatar' => ['/assets/avatar/<user>', ['res' => '<res>']], 'user-background' => ['/assets/profile-background/<user>'], 'settings-index' => ['/settings'], 'settings-account' => ['/settings/account.php'], 'settings-sessions' => ['/settings/sessions.php'], 'settings-logs' => ['/settings/logs.php'], 'settings-data' => ['/settings/data.php'], 'comment-create' => ['/comments.php', ['m' => 'create']], 'comment-vote' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'vote', 'v' => '<vote>']], 'comment-delete' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'delete']], 'comment-restore' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'restore']], 'comment-pin' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'pin']], 'comment-unpin' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'unpin']], 'manage-index' => ['/manage'], 'manage-general-overview' => ['/manage/general'], 'manage-general-logs' => ['/manage/general/logs.php'], 'manage-general-blacklist' => ['/manage/general/blacklist.php'], 'manage-general-emoticons' => ['/manage/general/emoticons.php'], 'manage-general-emoticon' => ['/manage/general/emoticon.php', ['e' => '<emote>']], 'manage-general-emoticon-order-up' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'd', 'csrf' => '{token}']], 'manage-general-emoticon-order-down'=> ['/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'i', 'csrf' => '{token}']], 'manage-general-emoticon-delete' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'delete' => '1', 'csrf' => '{token}']], 'manage-general-emoticon-alias' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'alias' => '<string>', 'csrf' => '{token}']], 'manage-general-settings' => ['/manage/general/settings.php'], 'manage-general-setting' => ['/manage/general/setting.php', ['name' => '<name>', 'type' => '<type>']], 'manage-general-setting-delete' => ['/manage/general/setting-delete.php', ['name' => '<name>']], 'manage-forum-categories' => ['/manage/forum/index.php'], 'manage-forum-category' => ['/manage/forum/category.php', ['f' => '<forum>']], 'manage-changelog-changes' => ['/manage/changelog'], 'manage-changelog-change' => ['/manage/changelog/change.php', ['c' => '<change>']], 'manage-changelog-tags' => ['/manage/changelog/tags.php'], 'manage-changelog-tag' => ['/manage/changelog/tag.php', ['t' => '<tag>']], 'manage-news-categories' => ['/manage/news/categories.php'], 'manage-news-category' => ['/manage/news/category.php', ['c' => '<category>']], 'manage-news-posts' => ['/manage/news/posts.php'], 'manage-news-post' => ['/manage/news/post.php', ['p' => '<post>']], 'manage-users' => ['/manage/users'], 'manage-user' => ['/manage/users/user.php', ['u' => '<user>']], 'manage-users-reports' => ['/manage/users/reports.php', ['u' => '<user>']], 'manage-users-report' => ['/manage/users/report.php', ['r' => '<report>']], 'manage-users-warnings' => ['/manage/users/warnings.php', ['u' => '<user>']], 'manage-users-warning-delete' => ['/manage/users/warnings.php', ['w' => '<warning>', 'delete' => '1', 'csrf' => '{csrf}']], 'manage-roles' => ['/manage/users/roles.php'], 'manage-role' => ['/manage/users/role.php', ['r' => '<role>']], ]); function url(string $name, array $variables = []): string { if(!array_key_exists($name, MSZ_URLS)) { return ''; } $info = MSZ_URLS[$name]; if(!isset($info[0]) || !is_string($info[0])) { return ''; } $splitUrl = explode('/', $info[0]); for($i = 0; $i < count($splitUrl); $i++) { $splitUrl[$i] = url_variable($splitUrl[$i], $variables); } $url = implode('/', $splitUrl); if(!empty($info[1]) && is_array($info[1])) { $url .= '?'; foreach($info[1] as $key => $value) { $value = url_variable($value, $variables); if(empty($value) || ($key === 'page' && $value < 2)) continue; $url .= sprintf('%s=%s&', $key, $value); } $url = trim($url, '?&'); } if(!empty($info[2]) && is_string($info[2])) { $url .= rtrim(sprintf('#%s', url_variable($info[2], $variables)), '#'); } return $url; } function redirect(string $url): void { header('Location: ' . $url); } function url_redirect(string $name, array $variables = []): void { redirect(url($name, $variables)); } function url_variable(string $value, array $variables): string { if(str_starts_with($value, '<') && str_ends_with($value, '>')) return $variables[trim($value, '<>')] ?? ''; if(str_starts_with($value, '[') && str_ends_with($value, ']')) return constant(trim($value, '[]')); if(str_starts_with($value, '{') && str_ends_with($value, '}')) return \Misuzu\CSRF::token(); // Hack that allows variables with file extensions $pathInfo = pathinfo($value); if($value !== $pathInfo['filename']) { $fallback = url_variable($pathInfo['filename'], $variables); if($fallback !== $pathInfo['filename']) return $fallback . '.' . $pathInfo['extension']; } return $value; } function url_construct(string $url, array $query = [], ?string $fragment = null): string { if(count($query)) { $url .= mb_strpos($url, '?') !== false ? '&' : '?'; foreach($query as $key => $value) { if($value) { $url .= rawurlencode($key) . '=' . rawurlencode($value) . '&'; } } $url = mb_substr($url, 0, -1); } if(!empty($fragment)) { $url .= "#{$fragment}"; } return $url; } function url_prefix(bool $trailingSlash = true): string { return 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . ($trailingSlash ? '/' : ''); } function is_local_url(string $url): bool { $length = mb_strlen($url); if($length < 1) return false; if($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true)) return true; return str_starts_with($url, url_prefix()); }