Ran PHPStan over everything and attempted some fixes.

This commit is contained in:
flash 2022-02-19 19:03:00 +00:00
parent 3a118fef15
commit d55585372e
49 changed files with 115 additions and 195 deletions

View file

@ -1,104 +0,0 @@
Misuzu.UserRelations = {};
Misuzu.UserRelations.Type = DefineEnum({ none: 0, follow: 1, });
Misuzu.UserRelations.init = function() {
var buttons = document.getElementsByClassName('js-user-relation-action');
for(var i = 0; i < buttons.length; ++i) {
switch(buttons[i].tagName.toLowerCase()) {
case 'a':
buttons[i].removeAttribute('href');
buttons[i].removeAttribute('target');
buttons[i].removeAttribute('rel');
break;
}
buttons[i].addEventListener('click', Misuzu.UserRelations.setRelationHandler);
}
};
Misuzu.UserRelations.setRelation = function(user, type, onSuccess, onFailure) {
var xhr = new XMLHttpRequest;
xhr.addEventListener('readystatechange', function() {
if(xhr.readyState !== 4)
return;
Misuzu.CSRF.setToken(xhr.getResponseHeader('X-Misuzu-CSRF'));
var json = JSON.parse(xhr.responseText),
message = json.error || json.message;
if(message && onFailure)
onFailure(message);
else if(!message && onSuccess)
onSuccess(json);
});
xhr.open('GET', Misuzu.Urls.format('user-relation-create', [Misuzu.Urls.v('user', user), Misuzu.Urls.v('type', type)]));
xhr.setRequestHeader('X-Misuzu-XHR', 'user_relation');
xhr.setRequestHeader('X-Misuzu-CSRF', Misuzu.CSRF.getToken());
xhr.send();
};
Misuzu.UserRelations.ICO_ADD = 'fas fa-user-plus';
Misuzu.UserRelations.ICO_REM = 'fas fa-user-minus';
Misuzu.UserRelations.ICO_BUS = 'fas fa-spinner fa-pulse';
Misuzu.UserRelations.BTN_BUS = 'input__button--busy';
Misuzu.UserRelations.setRelationHandler = function(ev) {
var target = this,
userId = parseInt(target.dataset.relationUser),
relationType = parseInt(target.dataset.relationType),
isButton = target.classList.contains('input__button'),
icon = target.querySelector('[class^="fa"]');
if(isButton) {
if(target.classList.contains(Misuzu.UserRelations.BTN_BUS))
return;
target.classList.add(Misuzu.UserRelations.BTN_BUS);
}
if(icon)
icon.className = Misuzu.UserRelations.ICO_BUS;
Misuzu.UserRelations.setRelation(
userId,
relationType,
function(info) {
target.classList.remove(Misuzu.UserRelations.BTN_BUS);
switch(info.relation_type) {
case Misuzu.UserRelations.Type.none:
if(isButton) {
if(target.classList.contains('input__button--destroy'))
target.classList.remove('input__button--destroy');
target.textContent = 'Follow';
}
if(icon) {
icon.className = Misuzu.UserRelations.ICO_ADD;
target.title = 'Follow';
}
target.dataset.relationType = Misuzu.UserRelations.Type.follow.toString();
break;
case Misuzu.UserRelations.Type.follow:
if(isButton) {
if(!target.classList.contains('input__button--destroy'))
target.classList.add('input__button--destroy');
target.textContent = 'Unfollow';
}
if(icon) {
icon.className = Misuzu.UserRelations.ICO_REM;
target.title = 'Unfollow';
}
target.dataset.relationType = Misuzu.UserRelations.Type.none.toString();
break;
}
},
function(msg) {
target.classList.remove(Misuzu.UserRelations.BTN_BUS);
Misuzu.showMessageBox(msg);
}
);
};

View file

@ -242,7 +242,7 @@ if(parse_url($_SERVER['PHP_SELF'], PHP_URL_PATH) !== '/index.php')
if(!empty($userInfo)) if(!empty($userInfo))
Template::set('current_user', $userInfo); Template::set('current_user', $userInfo);
$inManageMode = starts_with($_SERVER['REQUEST_URI'], '/manage'); $inManageMode = str_starts_with($_SERVER['REQUEST_URI'], '/manage');
$hasManageAccess = User::hasCurrent() $hasManageAccess = User::hasCurrent()
&& !User::getCurrent()->hasActiveWarning() && !User::getCurrent()->hasActiveWarning()
&& perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_CAN_MANAGE); && perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_CAN_MANAGE);

6
phpstan.neon Normal file
View file

@ -0,0 +1,6 @@
parameters:
level: 5
paths:
- src
bootstrapFiles:
- misuzu.php

View file

@ -149,7 +149,7 @@ class AuditLog {
public static function create(string $action, array $params = [], ?User $user = null, ?string $remoteAddr = null): void { public static function create(string $action, array $params = [], ?User $user = null, ?string $remoteAddr = null): void {
$user = $user ?? User::getCurrent(); $user = $user ?? User::getCurrent();
$remoteAddr = $ipAddress ?? IPAddress::remote(); $remoteAddr = $remoteAddr ?? IPAddress::remote();
$createLog = DB::prepare( $createLog = DB::prepare(
'INSERT INTO `' . DB::PREFIX . self::TABLE . '` (`log_action`, `user_id`, `log_params`, `log_ip`, `log_country`)' 'INSERT INTO `' . DB::PREFIX . self::TABLE . '` (`log_action`, `user_id`, `log_params`, `log_ip`, `log_country`)'
. ' VALUES (:action, :user, :params, INET6_ATON(:ip), :country)' . ' VALUES (:action, :user, :params, INET6_ATON(:ip), :country)'
@ -162,7 +162,7 @@ class AuditLog {
} }
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countAll(?User $user = null): int { public static function countAll(?User $user = null): int {
$getCount = DB::prepare( $getCount = DB::prepare(

View file

@ -65,7 +65,7 @@ class AuthToken {
} }
public static function unpack(string $data, bool $base64 = true): self { public static function unpack(string $data, bool $base64 = true): self {
$obj = new static; $obj = new AuthToken;
if(empty($data)) if(empty($data))
return $obj; return $obj;
@ -83,7 +83,7 @@ class AuthToken {
} }
public static function create(User $user, UserSession $session): self { public static function create(User $user, UserSession $session): self {
return (new static) return (new AuthToken)
->setUser($user) ->setUser($user)
->setSession($session); ->setSession($session);
} }

View file

@ -183,7 +183,7 @@ class ChangelogChange implements JsonSerializable {
return [ return [
'id' => $this->getId(), 'id' => $this->getId(),
'user' => $this->getUserId(), 'user' => $this->getUserId(),
'action' => $this->getActionId(), 'action' => $this->getAction(),
'header' => $this->getHeader(), 'header' => $this->getHeader(),
'body' => $this->getBody(), 'body' => $this->getBody(),
'comments' => $this->getCommentsCategoryName(), 'comments' => $this->getCommentsCategoryName(),

View file

@ -78,7 +78,7 @@ class ChangelogChangeTag implements JsonSerializable {
private static function byQueryBase(): string { private static function byQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE)); return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
} }
public static function byExact(ChangelogChange $change, ChangelogTag $tag): array { public static function byExact(ChangelogChange $change, ChangelogTag $tag): self {
$tag = DB::prepare(self::byQueryBase() . ' WHERE `tag_id` = :tag') $tag = DB::prepare(self::byQueryBase() . ' WHERE `tag_id` = :tag')
->bind('change', $change->getId()) ->bind('change', $change->getId())
->bind('tag', $tag->getId()) ->bind('tag', $tag->getId())

View file

@ -18,14 +18,14 @@ class Colour {
} }
public static function none(): self { public static function none(): self {
return new static(self::FLAG_INHERIT); return new Colour(self::FLAG_INHERIT);
} }
public static function fromRgb(int $red, int $green, int $blue): self { public static function fromRgb(int $red, int $green, int $blue): self {
return (new static)->setRed($red)->setGreen($green)->setBlue($blue); return (new Colour)->setRed($red)->setGreen($green)->setBlue($blue);
} }
public static function fromHex(string $hex): self { public static function fromHex(string $hex): self {
return (new static)->setHex($hex); return (new Colour)->setHex($hex);
} }
public function getRaw(): int { public function getRaw(): int {
@ -99,7 +99,7 @@ class Colour {
return $this; return $this;
} }
public function getLuminance(): int { public function getLuminance(): float {
return self::LUMINANCE_WEIGHT_RED * $this->getRed() return self::LUMINANCE_WEIGHT_RED * $this->getRed()
+ self::LUMINANCE_WEIGHT_GREEN * $this->getGreen() + self::LUMINANCE_WEIGHT_GREEN * $this->getGreen()
+ self::LUMINANCE_WEIGHT_BLUE * $this->getBlue(); + self::LUMINANCE_WEIGHT_BLUE * $this->getBlue();

View file

@ -128,7 +128,7 @@ class CommentsVote implements JsonSerializable {
} }
private static function fake(CommentsPost $post, User $user, int $vote): CommentsVote { private static function fake(CommentsPost $post, User $user, int $vote): CommentsVote {
$fake = new static; $fake = new CommentsVote;
$fake->comment_id = $post->getId(); $fake->comment_id = $post->getId();
$fake->comment = $post; $fake->comment = $post;
$fake->user_id = $user->getId(); $fake->user_id = $user->getId();

View file

@ -4,6 +4,14 @@ namespace Misuzu;
use PDO; use PDO;
use Misuzu\Database\Database; use Misuzu\Database\Database;
/**
* @method static PDO getPDO()
* @method static int queries()
* @method static int exec(string $stmt)
* @method static \Misuzu\Database\DatabaseStatement prepare(string $stmt, array $options = [])
* @method static \Misuzu\Database\DatabaseStatement query(string $stmt, ?int $fetchMode = null, ...$args)
* @method static int lastId()
*/
final class DB { final class DB {
private static $instance; private static $instance;

View file

@ -1,7 +1,14 @@
<?php <?php
namespace Misuzu; namespace Misuzu;
use InvalidArgumentException;
final class Emoticon { final class Emoticon {
private int $emote_id;
private int $emote_order;
private int $emote_hierarchy;
private string $emote_url;
public const ALL = PHP_INT_MAX; public const ALL = PHP_INT_MAX;
public function __construct() { public function __construct() {
@ -132,7 +139,7 @@ final class Emoticon {
public static function byId(int $emoteId): self { public static function byId(int $emoteId): self {
if($emoteId < 1) if($emoteId < 1)
return []; throw new InvalidArgumentException('$emoteId is not a valid emoticon id.');
$getEmote = DB::prepare(' $getEmote = DB::prepare('
SELECT `emote_id`, `emote_order`, `emote_hierarchy`, `emote_url` SELECT `emote_id`, `emote_order`, `emote_hierarchy`, `emote_url`

View file

@ -70,8 +70,6 @@ class Feed {
return count($this->feedItems) > 0; return count($this->feedItems) > 0;
} }
public function addItem(FeedItem $item): self { public function addItem(FeedItem $item): self {
if($item === null)
throw new InvalidArgumentException('item may not be null');
$this->feedItems[] = $item; $this->feedItems[] = $item;
return $this; return $this;
} }

View file

@ -238,9 +238,8 @@ function forum_get_child_ids(int $forumId): array {
} }
function forum_topics_unread(int $forumId, int $userId): int { function forum_topics_unread(int $forumId, int $userId): int {
if($userId < 1 || $forumId < 1) { if($userId < 1 || $forumId < 1)
return false; return 0;
}
static $memoized = []; static $memoized = [];
$memoId = "{$forumId}-{$userId}"; $memoId = "{$forumId}-{$userId}";

View file

@ -308,7 +308,7 @@ function forum_post_can_delete($postId, ?int $userId = null): int {
return MSZ_E_FORUM_POST_DELETE_OWNER; return MSZ_E_FORUM_POST_DELETE_OWNER;
} }
if(strtotime($post['post_created']) <= time() - MSZ_FORUM_POST_DELETE_LIMIT) { if(strtotime($post['post_created']) <= (time() - MSZ_FORUM_POST_DELETE_LIMIT)) {
return MSZ_E_FORUM_POST_DELETE_OLD; return MSZ_E_FORUM_POST_DELETE_OLD;
} }
} }

View file

@ -575,7 +575,7 @@ function forum_topic_can_delete($topicId, ?int $userId = null): int {
return MSZ_E_FORUM_TOPIC_DELETE_OWNER; return MSZ_E_FORUM_TOPIC_DELETE_OWNER;
} }
if(strtotime($topic['topic_created']) <= time() - MSZ_FORUM_TOPIC_DELETE_TIME_LIMIT) { if(strtotime($topic['topic_created']) <= (time() - MSZ_FORUM_TOPIC_DELETE_TIME_LIMIT)) {
return MSZ_E_FORUM_TOPIC_DELETE_OLD; return MSZ_E_FORUM_TOPIC_DELETE_OLD;
} }

View file

@ -26,7 +26,7 @@ class ChangelogHandler extends Handler {
if($filterDate !== null) if($filterDate !== null)
try { try {
$dateParts = explode('-', $filterDate, 3); $dateParts = explode('-', $filterDate, 3);
$filterDate = gmmktime(12, 0, 0, $dateParts[1], $dateParts[2], $dateParts[0]); $filterDate = gmmktime(12, 0, 0, intval($dateParts[1]), intval($dateParts[2]), intval($dateParts[0]));
} catch(ErrorException $ex) { } catch(ErrorException $ex) {
return 404; return 404;
} }

View file

@ -11,7 +11,7 @@ final class ForumHandler extends Handler {
$forumId = (int)$request->getQueryParam('forum', FILTER_SANITIZE_NUMBER_INT); $forumId = (int)$request->getQueryParam('forum', FILTER_SANITIZE_NUMBER_INT);
$response->setTemplate('confirm', [ $response->setTemplate('confirm', [
'title' => 'Mark forum as read', 'title' => 'Mark forum as read',
'message' => 'Are you sure you want to mark ' . ($forumId === null ? 'the entire' : 'this') . ' forum as read?', 'message' => 'Are you sure you want to mark ' . ($forumId === 0 ? 'the entire' : 'this') . ' forum as read?',
'return' => url($forumId ? 'forum-category' : 'forum-index', ['forum' => $forumId]), 'return' => url($forumId ? 'forum-category' : 'forum-index', ['forum' => $forumId]),
'params' => [ 'params' => [
'forum' => $forumId, 'forum' => $forumId,

View file

@ -20,7 +20,7 @@ final class InfoHandler extends Handler {
'title' => '', 'title' => '',
]; ];
$isMisuzuDoc = $name === 'misuzu' || starts_with($name, 'misuzu/'); $isMisuzuDoc = $name === 'misuzu' || str_starts_with($name, 'misuzu/');
if($isMisuzuDoc) { if($isMisuzuDoc) {
$filename = substr($name, 7); $filename = substr($name, 7);
@ -41,8 +41,8 @@ final class InfoHandler extends Handler {
if(empty($document['content'])) if(empty($document['content']))
return 404; return 404;
if(empty($document['title'])) { if($document['title'] === '') {
if(starts_with($document['content'], '# ')) { if(str_starts_with($document['content'], '# ')) {
$titleOffset = strpos($document['content'], "\n"); $titleOffset = strpos($document['content'], "\n");
$document['title'] = trim(substr($document['content'], 2, $titleOffset - 1)); $document['title'] = trim(substr($document['content'], 2, $titleOffset - 1));
$document['content'] = substr($document['content'], $titleOffset); $document['content'] = substr($document['content'], $titleOffset);

View file

@ -13,7 +13,7 @@ use Misuzu\Feeds\RssFeedSerializer;
use Misuzu\News\NewsCategory; use Misuzu\News\NewsCategory;
use Misuzu\News\NewsPost; use Misuzu\News\NewsPost;
use Misuzu\News\NewsCategoryNotFoundException; use Misuzu\News\NewsCategoryNotFoundException;
use Misuzu\News\NewsPostNotException; use Misuzu\News\NewsPostNotFoundException;
use Misuzu\Parsers\Parser; use Misuzu\Parsers\Parser;
use Misuzu\Users\User; use Misuzu\Users\User;

View file

@ -67,12 +67,14 @@ final class SockChatHandler extends Handler {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
if(isset($body['bump'], $body['hash']) && is_string($body['bump']) && is_string($body['hash'])) if(isset($body['bump'], $body['hash']) && is_string($body['bump']) && is_string($body['hash'])) {
return $this->bump( $this->bump(
$response, $response,
$request->setHeader('X-SharpChat-Signature', $body['hash']) $request->setHeader('X-SharpChat-Signature', $body['hash'])
->setBody(Stream::create($body['bump'])) ->setBody(Stream::create($body['bump']))
); );
return;
}
$source = isset($body['user_id']) ? $body : $query; $source = isset($body['user_id']) ? $body : $query;
@ -381,9 +383,10 @@ final class SockChatHandler extends Handler {
$userInfo = User::byUsername($param); $userInfo = User::byUsername($param);
break; break;
} }
} catch(UserNotFoundException $ex) { } catch(UserNotFoundException $ex) {}
if(!isset($userInfo))
return []; return [];
}
return [ return [
'user_id' => $userInfo->getId(), 'user_id' => $userInfo->getId(),

View file

@ -16,7 +16,7 @@ class AcceptHttpHeaderChild implements HasQualityInterface {
if(isset($parts[1])) { if(isset($parts[1])) {
$split = explode('=', $parts[1], 2); $split = explode('=', $parts[1], 2);
if($split[0] === 'q' && isset($split[1])) if($split[0] === 'q' && isset($split[1]))
$this->quality = max(min(round(filter_var($split[1], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) ?? 1, 2), 1), 0); $this->quality = max(min(round((float)filter_var($split[1], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION), 2), 1), 0);
} }
} }

View file

@ -52,7 +52,7 @@ class HttpHeader {
case 'TE': case 'TE':
return new AcceptHttpHeader($name, $line); return new AcceptHttpHeader($name, $line);
default: default:
return new static($name, $line); return new HttpHeader($name, $line);
} }
} }
} }

View file

@ -190,7 +190,7 @@ class HttpRequestMessage extends HttpMessage {
} }
} }
if(!isset($headers['Authorization'])) { if(empty($headers['Authorization'])) {
if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
} elseif(isset($_SERVER['PHP_AUTH_USER'])) { } elseif(isset($_SERVER['PHP_AUTH_USER'])) {

View file

@ -131,7 +131,6 @@ class HttpResponseMessage extends HttpMessage {
414 => 'URI Too Long', 414 => 'URI Too Long',
416 => 'Range Not Satisfiable', 416 => 'Range Not Satisfiable',
417 => 'Expectation Failed', 417 => 'Expectation Failed',
417 => 'Expectation Failed',
418 => 'I\'m a teapot', 418 => 'I\'m a teapot',
421 => 'Misdirected Request', 421 => 'Misdirected Request',
422 => 'Unprocessable Entity', 422 => 'Unprocessable Entity',

View file

@ -23,7 +23,7 @@ class Route {
} }
public static function create(array $methods, string $path, ?string $method = null, ?string $class = null): self { public static function create(array $methods, string $path, ?string $method = null, ?string $class = null): self {
return new static($methods, $path, $method, $class); return new Route($methods, $path, $method, $class);
} }
public static function get(string $path, ?string $method = null, ?string $class = null): self { public static function get(string $path, ?string $method = null, ?string $class = null): self {
return self::create(['GET'], $path, $method, $class); return self::create(['GET'], $path, $method, $class);

View file

@ -21,7 +21,7 @@ class Router {
if($name[0] === '_') if($name[0] === '_')
return null; return null;
if(self::$instance === null) if(self::$instance === null)
(new static)->setInstance(); (new Router)->setInstance();
return self::$instance->{'_' . $name}(...$args); return self::$instance->{'_' . $name}(...$args);
} }
@ -92,7 +92,7 @@ class Router {
$handlerMethod = $route->getHandlerMethod(); $handlerMethod = $route->getHandlerMethod();
if(is_callable($handlerMethod)) { if(is_callable($handlerMethod)) {
$result = call_user_func_array($handlerMethod, $args); $result = call_user_func_array($handlerMethod, $matches);
} elseif($handlerMethod[0] === '/') { } elseif($handlerMethod[0] === '/') {
$response->redirect($handlerMethod); $response->redirect($handlerMethod);
} else { } else {

View file

@ -38,7 +38,7 @@ class UploadedFile {
$this->stream = $fileNameOrStream; $this->stream = $fileNameOrStream;
if($size === null && $this->stream !== null) if($size === null && $this->stream !== null)
$this->size = $stream->getSize(); $this->size = $this->stream->getSize();
} }
} }
@ -47,7 +47,7 @@ class UploadedFile {
throw new RuntimeException('Can\'t open stream because of an upload error.'); throw new RuntimeException('Can\'t open stream because of an upload error.');
if($this->hasMoved) if($this->hasMoved)
throw new RuntimeException('Can\'t open stream because file has already been moved.'); throw new RuntimeException('Can\'t open stream because file has already been moved.');
if($this->steam === null) if($this->stream === null)
$this->stream = Stream::createFromFile($this->fileName); $this->stream = Stream::createFromFile($this->fileName);
return $this->stream; return $this->stream;
@ -97,7 +97,7 @@ class UploadedFile {
} }
public static function createFromFILE(array $file): self { public static function createFromFILE(array $file): self {
return new static( return new UploadedFile(
$file['tmp_name'] ?? '', $file['tmp_name'] ?? '',
$file['error'] ?? UPLOAD_ERR_NO_FILE, $file['error'] ?? UPLOAD_ERR_NO_FILE,
$file['size'] ?? null, $file['size'] ?? null,

View file

@ -26,7 +26,7 @@ final class GdImage extends Image {
]; ];
public function __construct($pathOrWidth, int $height = -1) { public function __construct($pathOrWidth, int $height = -1) {
parent::__construct($pathOrWidth, $height); parent::__construct($pathOrWidth);
if(is_int($pathOrWidth)) { if(is_int($pathOrWidth)) {
$this->gd = imagecreatetruecolor($pathOrWidth, $height < 1 ? $pathOrWidth : $height); $this->gd = imagecreatetruecolor($pathOrWidth, $height < 1 ? $pathOrWidth : $height);

View file

@ -5,7 +5,7 @@ use InvalidArgumentException;
use UnexpectedValueException; use UnexpectedValueException;
abstract class Image { abstract class Image {
public function __construct($pathOrWidth, int $height = -1) { public function __construct($pathOrWidth) {
if(!is_int($pathOrWidth) && !is_string($pathOrWidth)) if(!is_int($pathOrWidth) && !is_string($pathOrWidth))
throw new InvalidArgumentException('The first argument must be or type string to open an image file, or int to set a width for a new image.'); throw new InvalidArgumentException('The first argument must be or type string to open an image file, or int to set a width for a new image.');
} }

View file

@ -8,12 +8,12 @@ final class ImagickImage extends Image {
private ?Imagick $imagick = null; private ?Imagick $imagick = null;
public function __construct($pathOrWidth, int $height = -1) { public function __construct($pathOrWidth, int $height = -1) {
parent::__construct($pathOrWidth, $height); parent::__construct($pathOrWidth);
if(is_int($pathOrWidth)) { if(is_int($pathOrWidth)) {
$this->imagick = new Imagick(); $this->imagick = new Imagick();
$this->newImage($pathOrWidth, $height < 1 ? $pathOrWidth : $height, 'none'); $this->imagick->newImage($pathOrWidth, $height < 1 ? $pathOrWidth : $height, 'none');
$this->setImageFormat('png'); $this->imagick->setImageFormat('png');
} elseif(is_string($pathOrWidth)) { } elseif(is_string($pathOrWidth)) {
$imagick = new Imagick($pathOrWidth); $imagick = new Imagick($pathOrWidth);
$imagick->setImageFormat($imagick->getNumberImages() > 1 ? 'gif' : 'png'); $imagick->setImageFormat($imagick->getNumberImages() > 1 ? 'gif' : 'png');

View file

@ -14,6 +14,9 @@ final class Mailer {
private static $dsn = 'null://null'; private static $dsn = 'null://null';
private static $transport = null; private static $transport = null;
private static string $senderName = 'Flashii';
private static string $senderAddr = 'sys@flashii.net';
public static function init(string $method, array $config): void { public static function init(string $method, array $config): void {
if($method !== 'smtp') { if($method !== 'smtp') {
self::$dsn = 'null://null'; self::$dsn = 'null://null';
@ -42,6 +45,12 @@ final class Mailer {
$dsn .= $config['port'] ?? 25; $dsn .= $config['port'] ?? 25;
self::$dsn = $dsn; self::$dsn = $dsn;
if(!empty($config['sender_name']))
self::$senderName = $config['sender_name'];
if(!empty($config['sender_addr']))
self::$senderAddr = $config['sender_addr'];
} }
public static function getTransport() { public static function getTransport() {
@ -59,8 +68,8 @@ final class Mailer {
$message = new SymfonyMessage; $message = new SymfonyMessage;
$message->from(new SymfonyAddress( $message->from(new SymfonyAddress(
$config['sender_addr'] ?? 'sys@flashii.net', self::$senderAddr,
$config['sender_name'] ?? 'Flashii' self::$senderName
)); ));
if($bcc) if($bcc)

View file

@ -68,7 +68,7 @@ class MediaType implements HasQualityInterface {
} }
public function matchPattern(string $pattern): bool { public function matchPattern(string $pattern): bool {
try { try {
$mediaType = new static($pattern); $mediaType = new MediaType($pattern);
} catch(InvalidArgumentException $ex) { } catch(InvalidArgumentException $ex) {
return false; return false;
} }

View file

@ -27,7 +27,7 @@ final class Pagination {
} }
public function getPages(): int { public function getPages(): int {
return ceil($this->getCount() / $this->getRange()); return (int)ceil($this->getCount() / $this->getRange());
} }
public function hasValidOffset(): bool { public function hasValidOffset(): bool {
@ -50,7 +50,7 @@ final class Pagination {
if($this->getOffset() < 1) if($this->getOffset() < 1)
return self::START_PAGE; return self::START_PAGE;
return floor($this->getOffset() / $this->getRange()) + self::START_PAGE; return (int)floor($this->getOffset() / $this->getRange()) + self::START_PAGE;
} }
public function setPage(int $page, bool $zeroBased = false): self { public function setPage(int $page, bool $zeroBased = false): self {
@ -70,7 +70,7 @@ final class Pagination {
$source ??= $_GET; $source ??= $_GET;
if(isset($source[$name]) && is_string($source[$name]) && ctype_digit($source[$name])) if(isset($source[$name]) && is_string($source[$name]) && ctype_digit($source[$name]))
return $source[$name]; return intval($source[$name]);
return $default; return $default;
} }

View file

@ -47,7 +47,7 @@ class Stream {
if($contents instanceof Stream) if($contents instanceof Stream)
return $contents; return $contents;
return new static($contents); return new Stream($contents);
} }
public static function createFromFile(string $filename, string $mode = 'rb'): Stream { public static function createFromFile(string $filename, string $mode = 'rb'): Stream {
if(!in_array($mode[0], ['r', 'w', 'a', 'x', 'c'])) if(!in_array($mode[0], ['r', 'w', 'a', 'x', 'c']))
@ -79,9 +79,9 @@ class Stream {
return $this->readable; return $this->readable;
} }
public function read($length): int { public function read($length): string {
if(!$this->isReadable()) if(!$this->isReadable())
throw RuntimeException('Can\'t read from this stream.'); throw new RuntimeException('Can\'t read from this stream.');
return fread($this->stream, $length); return fread($this->stream, $length);
} }

View file

@ -32,6 +32,6 @@ class TOTP {
$bin |= (ord($hash[$offset + 3]) & 0xFF); $bin |= (ord($hash[$offset + 3]) & 0xFF);
$otp = $bin % pow(10, self::DIGITS); $otp = $bin % pow(10, self::DIGITS);
return str_pad($otp, self::DIGITS, STR_PAD_LEFT); return str_pad(strval($otp), self::DIGITS, '0', STR_PAD_LEFT);
} }
} }

View file

@ -34,7 +34,7 @@ final class Template {
define('MSZ_TPL_RENDER', microtime(true)); define('MSZ_TPL_RENDER', microtime(true));
} }
if(!ends_with($file, self::FILE_EXT)) { if(!str_ends_with($file, self::FILE_EXT)) {
$file = str_replace('.', DIRECTORY_SEPARATOR, $file) . self::FILE_EXT; $file = str_replace('.', DIRECTORY_SEPARATOR, $file) . self::FILE_EXT;
} }

View file

@ -57,7 +57,7 @@ class UserAvatarAsset extends UserImageAsset implements UserAssetScalableInterfa
} }
public function getScaledFileExtension(int $dims): string { public function getScaledFileExtension(int $dims): string {
$imageSize = getimagesize($this->getScaledPath($dims)); $imageSize = getimagesize($this->getScaledPath($dims));
if($imageSize === null) if($imageSize === false)
return 'img'; return 'img';
return self::TYPES_EXT[$imageSize[2]] ?? 'img'; return self::TYPES_EXT[$imageSize[2]] ?? 'img';
} }

View file

@ -94,7 +94,7 @@ class UserBackgroundAsset extends UserImageAsset {
return $this; return $this;
} }
public function isBlend(): bool { public function isBlend(): bool {
return $this->getAttributes() & self::ATTRIB_BLEND; return ($this->getAttributes() & self::ATTRIB_BLEND) > 0;
} }
public function setBlend(bool $blend): self { public function setBlend(bool $blend): self {
$this->getUser()->setBackgroundSettings( $this->getUser()->setBackgroundSettings(
@ -105,7 +105,7 @@ class UserBackgroundAsset extends UserImageAsset {
return $this; return $this;
} }
public function isSlide(): bool { public function isSlide(): bool {
return $this->getAttributes() & self::ATTRIB_SLIDE; return ($this->getAttributes() & self::ATTRIB_SLIDE) > 0;
} }
public function setSlide(bool $slide): self { public function setSlide(bool $slide): self {
$this->getUser()->setBackgroundSettings( $this->getUser()->setBackgroundSettings(

View file

@ -120,7 +120,7 @@ abstract class UserImageAsset implements JsonSerializable, UserImageAssetInterfa
public function setFromData(string $data): void { public function setFromData(string $data): void {
$file = tempnam(sys_get_temp_dir(), 'msz'); $file = tempnam(sys_get_temp_dir(), 'msz');
if($file === null || !is_file($file)) if($file === false || !is_file($file))
throw new UserImageAssetFileCreationFailedException; throw new UserImageAssetFileCreationFailedException;
chmod($file, 0664); chmod($file, 0664);
file_put_contents($file, $data); file_put_contents($file, $data);

View file

@ -790,7 +790,7 @@ class User implements HasRankInterface, JsonSerializable {
************/ ************/
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countAll(bool $showDeleted = false): int { public static function countAll(bool $showDeleted = false): int {
return (int)DB::prepare( return (int)DB::prepare(

View file

@ -93,7 +93,7 @@ class UserLoginAttempt {
} }
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countAll(?User $user = null): int { public static function countAll(?User $user = null): int {
$getCount = DB::prepare( $getCount = DB::prepare(

View file

@ -83,7 +83,7 @@ class UserRole implements ArrayAccess, HasRankInterface {
public function getCanLeave(): bool { public function getCanLeave(): bool {
return boolval($this->role_can_leave); return boolval($this->role_can_leave);
} }
public function setCanLeave(bool $canLeave): bool { public function setCanLeave(bool $canLeave): self {
$this->role_can_leave = $canLeave ? 1 : 0; $this->role_can_leave = $canLeave ? 1 : 0;
return $this; return $this;
} }
@ -159,7 +159,7 @@ class UserRole implements ArrayAccess, HasRankInterface {
} }
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countAll(bool $showHidden = false): int { public static function countAll(bool $showHidden = false): int {
return (int)DB::prepare( return (int)DB::prepare(

View file

@ -60,7 +60,7 @@ class UserRoleRelation {
->execute(); ->execute();
// data is predictable, just create a "fake" // data is predictable, just create a "fake"
$object = new static; $object = new UserRoleRelation;
$object->user = $user; $object->user = $user;
$object->user_id = $user->getId(); $object->user_id = $user->getId();
$object->role = $role; $object->role = $role;
@ -69,7 +69,7 @@ class UserRoleRelation {
} }
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countUsers(UserRole $role): int { public static function countUsers(UserRole $role): int {
return (int)DB::prepare(self::countQueryBase() . ' WHERE `role_id` = :role') return (int)DB::prepare(self::countQueryBase() . ' WHERE `role_id` = :role')

View file

@ -199,7 +199,7 @@ class UserSession {
} }
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countAll(?User $user = null): int { public static function countAll(?User $user = null): int {
$getCount = DB::prepare( $getCount = DB::prepare(

View file

@ -197,7 +197,7 @@ class UserWarning {
} }
private static function countQueryBase(): string { private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); return sprintf(self::QUERY_SELECT, 'COUNT(*)');
} }
public static function countByRemoteAddress(?string $address = null, bool $withDuration = true): int { public static function countByRemoteAddress(?string $address = null, bool $withDuration = true): int {
$address = $address ?? IPAddress::remote(); $address = $address ?? IPAddress::remote();

View file

@ -86,6 +86,7 @@ final class Zalgo {
continue; continue;
$str .= $char; $str .= $char;
$num_up = $num_mid = $num_down = 0;
switch($mode) { switch($mode) {
case self::MODE_MINI: case self::MODE_MINI:

View file

@ -74,17 +74,15 @@ function perms_get_key(string $prefix, string $suffix): string {
function perms_get_select(array $modes = MSZ_PERM_MODES, string $allow = MSZ_PERMS_ALLOW, string $deny = MSZ_PERMS_DENY): string { function perms_get_select(array $modes = MSZ_PERM_MODES, string $allow = MSZ_PERMS_ALLOW, string $deny = MSZ_PERMS_DENY): string {
$select = ''; $select = '';
if(empty($select)) { foreach($modes as $mode) {
foreach($modes as $mode) { $select .= sprintf(
$select .= sprintf( '(BIT_OR(`%1$s_perms_%2$s`) &~ BIT_OR(`%1$s_perms_%3$s`)) AS `%1$s`,',
'(BIT_OR(`%1$s_perms_%2$s`) &~ BIT_OR(`%1$s_perms_%3$s`)) AS `%1$s`,', $mode, $allow, $deny
$mode, $allow, $deny );
);
}
$select = substr($select, 0, -1);
} }
$select = substr($select, 0, -1);
return $select; return $select;
} }

View file

@ -132,7 +132,7 @@ function url(string $name, array $variables = []): string {
$info = MSZ_URLS[$name]; $info = MSZ_URLS[$name];
if(!is_string($info[0] ?? null)) { if(!isset($info[0]) || !is_string($info[0])) {
return ''; return '';
} }
@ -175,13 +175,13 @@ function url_redirect(string $name, array $variables = []): void {
} }
function url_variable(string $value, array $variables): string { function url_variable(string $value, array $variables): string {
if(starts_with($value, '<') && ends_with($value, '>')) if(str_starts_with($value, '<') && str_ends_with($value, '>'))
return $variables[trim($value, '<>')] ?? ''; return $variables[trim($value, '<>')] ?? '';
if(starts_with($value, '[') && ends_with($value, ']')) if(str_starts_with($value, '[') && str_ends_with($value, ']'))
return constant(trim($value, '[]')); return constant(trim($value, '[]'));
if(starts_with($value, '{') && ends_with($value, '}')) if(str_starts_with($value, '{') && str_ends_with($value, '}'))
return \Misuzu\CSRF::token(); return \Misuzu\CSRF::token();
// Hack that allows variables with file extensions // Hack that allows variables with file extensions
@ -201,7 +201,7 @@ function url_list(): array {
$collection = []; $collection = [];
foreach(MSZ_URLS as $name => $urlInfo) { foreach(MSZ_URLS as $name => $urlInfo) {
if(empty($hasManageAccess) && starts_with($name, 'manage-')) if(empty($hasManageAccess) && str_starts_with($name, 'manage-'))
continue; continue;
$item = [ $item = [
@ -271,5 +271,5 @@ function is_local_url(string $url): bool {
if($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true)) if($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true))
return true; return true;
return starts_with($url, url_prefix()); return str_starts_with($url, url_prefix());
} }

View file

@ -33,16 +33,12 @@ function clamp($num, int $min, int $max): int {
return max($min, min($max, intval($num))); return max($min, min($max, intval($num)));
} }
function starts_with(string $string, string $text, bool $multibyte = true): bool { function starts_with(string $string, string $text): bool {
$strlen = $multibyte ? 'mb_strlen' : 'strlen'; return str_starts_with($string, $text);
$substr = $multibyte ? 'mb_substr' : 'substr';
return $substr($string, 0, $strlen($text)) === $text;
} }
function ends_with(string $string, string $text, bool $multibyte = true): bool { function ends_with(string $string, string $text): bool {
$strlen = $multibyte ? 'mb_strlen' : 'strlen'; return str_ends_with($string, $text);
$substr = $multibyte ? 'mb_substr' : 'substr';
return $substr($string, 0 - $strlen($text)) === $text;
} }
function first_paragraph(string $text, string $delimiter = "\n"): string { function first_paragraph(string $text, string $delimiter = "\n"): string {