From 82a3ce25c271a07d56eb2b8e501dc207543e5420 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 20 Jan 2024 20:48:47 +0000 Subject: [PATCH] level 7 typing --- core/basepage.php | 2 +- core/dbengine.php | 2 +- core/extension.php | 8 +-- core/polyfills.php | 21 ++----- core/stdlib_ex.php | 88 ++++++++++++++++++++++++++++++ core/util.php | 15 ++++- ext/alias_editor/main.php | 2 +- ext/artists/main.php | 20 +++++-- ext/auto_tagger/main.php | 2 +- ext/autocomplete/main.php | 2 +- ext/blotter/theme.php | 6 +- ext/browser_search/main.php | 4 +- ext/bulk_add_csv/main.php | 2 +- ext/bulk_download/main.php | 2 +- ext/bulk_import_export/main.php | 8 +-- ext/comment/main.php | 6 +- ext/comment/theme.php | 4 +- ext/cron_uploader/main.php | 4 +- ext/danbooru_api/main.php | 2 +- ext/emoticons_list/main.php | 2 +- ext/eokm/main.php | 2 +- ext/et/main.php | 8 +-- ext/favorites/main.php | 2 +- ext/featured/main.php | 2 +- ext/forum/main.php | 2 +- ext/graphql/main.php | 8 ++- ext/handle_archive/main.php | 1 + ext/handle_cbz/main.php | 4 +- ext/handle_ico/main.php | 8 +-- ext/handle_svg/main.php | 10 ++-- ext/help_pages/main.php | 17 +++--- ext/home/main.php | 2 +- ext/image/main.php | 6 +- ext/ipban/main.php | 3 +- ext/link_image/main.php | 2 +- ext/link_image/theme.php | 2 +- ext/log_db/main.php | 4 +- ext/log_logstash/main.php | 2 +- ext/media/main.php | 29 +++++----- ext/mime/mime_type.php | 13 ++--- ext/notes/main.php | 24 ++++---- ext/notes/theme.php | 15 ++--- ext/numeric_score/main.php | 26 ++++----- ext/numeric_score/theme.php | 9 ++- ext/ouroboros_api/main.php | 55 +++++++++---------- ext/pools/main.php | 35 ++++++++---- ext/pools/theme.php | 14 +++-- ext/rating/main.php | 1 + ext/rating/theme.php | 4 +- ext/relationships/test.php | 42 +++++++------- ext/replace_file/main.php | 4 +- ext/replace_file/test.php | 6 +- ext/report_image/main.php | 5 +- ext/report_image/theme.php | 5 +- ext/resize/main.php | 8 +-- ext/rotate/main.php | 8 +-- ext/rss_comments/main.php | 2 +- ext/rss_images/main.php | 2 +- ext/s3/main.php | 2 +- ext/setup/main.php | 4 +- ext/sitemap/main.php | 6 +- ext/source_history/main.php | 2 +- ext/static_files/main.php | 2 +- ext/tag_categories/main.php | 2 +- ext/tag_editcloud/info.php | 2 +- ext/tag_editcloud/main.php | 2 +- ext/tag_history/main.php | 2 +- ext/tag_list/main.php | 6 +- ext/tips/main.php | 2 +- ext/transcode/main.php | 6 +- ext/transcode_video/main.php | 2 +- ext/update/main.php | 2 +- ext/upload/main.php | 6 +- ext/upload/test.php | 2 +- ext/upload/theme.php | 4 +- ext/user/main.php | 2 +- tests/phpstan.neon | 2 +- themes/danbooru/comment.theme.php | 2 +- themes/danbooru2/comment.theme.php | 2 +- themes/futaba/comment.theme.php | 2 +- themes/rule34v2/page.class.php | 2 +- 81 files changed, 383 insertions(+), 277 deletions(-) create mode 100644 core/stdlib_ex.php diff --git a/core/basepage.php b/core/basepage.php index d0ba5e1d..76696c65 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -308,7 +308,7 @@ class BasePage assert($this->file, "file should not be null with PageMode::FILE"); // https://gist.github.com/codler/3906826 - $size = false_throws(filesize($this->file)); // File size + $size = filesize_ex($this->file); // File size $length = $size; // Content length $start = 0; // Start byte $end = $size - 1; // End byte diff --git a/core/dbengine.php b/core/dbengine.php index 9855b733..53a28136 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -131,7 +131,7 @@ class PostgreSQL extends DBEngine // shimmie functions for export to sqlite function _unix_timestamp(string $date): int { - return false_throws(strtotime($date)); + return strtotime_ex($date); } function _now(): string { diff --git a/core/extension.php b/core/extension.php index 7a5640ee..5b1bfe2e 100644 --- a/core/extension.php +++ b/core/extension.php @@ -149,7 +149,7 @@ abstract class ExtensionInfo public string $name; public string $license; public string $description; - /** @var array */ + /** @var array */ public array $authors = []; /** @var string[] */ public array $dependencies = []; @@ -322,7 +322,7 @@ abstract class DataHandlerExtension extends Extension throw new UploadException("Invalid or corrupted file"); } - $existing = Image::by_hash(false_throws(md5_file($event->tmpname))); + $existing = Image::by_hash(md5_file_ex($event->tmpname)); if (!is_null($existing)) { if ($config->get_string(ImageConfig::UPLOAD_COLLISION_HANDLER) == ImageConfig::COLLISION_MERGE) { // Right now tags are the only thing that get merged, so @@ -343,8 +343,8 @@ abstract class DataHandlerExtension extends Extension assert(is_readable($filename)); $image = new Image(); $image->tmp_file = $filename; - $image->filesize = false_throws(filesize($filename)); - $image->hash = false_throws(md5_file($filename)); + $image->filesize = filesize_ex($filename); + $image->hash = md5_file_ex($filename); $image->filename = (($pos = strpos($event->metadata['filename'], '?')) !== false) ? substr($event->metadata['filename'], 0, $pos) : $event->metadata['filename']; $image->set_mime(MimeType::get_for_file($filename, get_file_ext($event->metadata["filename"]) ?? null)); if (empty($image->get_mime())) { diff --git a/core/polyfills.php b/core/polyfills.php index e31b7241..b57a2da6 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -79,7 +79,7 @@ function full_copy(string $source, string $target): void if (is_dir($source)) { @mkdir($target); - $d = false_throws(dir($source)); + $d = dir_ex($source); while (true) { $entry = $d->read(); @@ -627,24 +627,11 @@ function parse_to_milliseconds(string $input): int */ function autodate(string $date, bool $html = true): string { - $cpu = date('c', false_throws(strtotime($date))); - $hum = date('F j, Y; H:i', false_throws(strtotime($date))); + $cpu = date('c', strtotime_ex($date)); + $hum = date('F j, Y; H:i', strtotime_ex($date)); return ($html ? "" : $hum); } -/** - * @template T - * @param T|false $x - * @return T - */ -function false_throws(mixed $x): mixed -{ - if($x === false) { - throw new \Exception("Unexpected false"); - } - return $x; -} - /** * Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss ) */ @@ -738,7 +725,7 @@ function validate_input(array $inputs): array } elseif (in_array('bool', $flags)) { $outputs[$key] = bool_escape($value); } elseif (in_array('date', $flags)) { - $outputs[$key] = date("Y-m-d H:i:s", false_throws(strtotime(trim($value)))); + $outputs[$key] = date("Y-m-d H:i:s", strtotime_ex(trim($value))); } elseif (in_array('string', $flags)) { if (in_array('trim', $flags)) { $value = trim($value); diff --git a/core/stdlib_ex.php b/core/stdlib_ex.php new file mode 100644 index 00000000..0122d3fd --- /dev/null +++ b/core/stdlib_ex.php @@ -0,0 +1,88 @@ + $depth + */ +function json_encode_ex(mixed $value, int|null $flags = 0, int $depth = 512): string +{ + return false_throws(json_encode($value, $flags, $depth)); +} + +function strtotime_ex(string $time, int|null $now = null): int +{ + return false_throws(strtotime($time, $now)); +} + +function md5_file_ex(string $filename, bool|null $raw_output = false): string +{ + return false_throws(md5_file($filename, $raw_output)); +} + +/** + * @return string[] + */ +function glob_ex(string $pattern, int|null $flags = 0): array +{ + return false_throws(glob($pattern, $flags)); +} + +function file_get_contents_ex(string $filename): string +{ + return false_throws(file_get_contents($filename)); +} + +function filesize_ex(string $filename): int +{ + return false_throws(filesize($filename)); +} + +function inet_ntop_ex(string $in_addr): string +{ + return false_throws(inet_ntop($in_addr)); +} + +function inet_pton_ex(string $ip_address): string +{ + return false_throws(inet_pton($ip_address)); +} + +function dir_ex(string $directory): \Directory +{ + return false_throws(dir($directory)); +} + +function exec_ex(string $command): string +{ + return false_throws(exec($command)); +} + +function filter_var_ex(mixed $variable, int $filter = FILTER_DEFAULT, mixed $options = null): mixed +{ + return false_throws(filter_var($variable, $filter, $options)); +} diff --git a/core/util.php b/core/util.php index e948ff29..18ae26e3 100644 --- a/core/util.php +++ b/core/util.php @@ -169,7 +169,7 @@ function get_real_ip(): string if(is_trusted_proxy()) { if (isset($_SERVER['HTTP_X_REAL_IP'])) { - if(filter_var($ip, FILTER_VALIDATE_IP)) { + if(filter_var_ex($ip, FILTER_VALIDATE_IP)) { $ip = $_SERVER['HTTP_X_REAL_IP']; } } @@ -177,7 +177,7 @@ function get_real_ip(): string if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $last_ip = $ips[count($ips) - 1]; - if(filter_var($last_ip, FILTER_VALIDATE_IP)) { + if(filter_var_ex($last_ip, FILTER_VALIDATE_IP)) { $ip = $last_ip; } } @@ -194,7 +194,7 @@ function get_session_ip(Config $config): string { $mask = $config->get_string("session_hash_mask", "255.255.0.0"); $addr = get_real_ip(); - $addr = false_throws(inet_ntop(false_throws(inet_pton($addr)) & false_throws(inet_pton($mask)))); + $addr = inet_ntop_ex(inet_pton_ex($addr) & inet_pton_ex($mask)); return $addr; } @@ -796,3 +796,12 @@ function generate_key(int $length = 20): string return $randomString; } + +function shm_tempnam(string $prefix = ""): string +{ + if(!is_dir("data/temp")) { + mkdir("data/temp"); + } + $temp = false_throws(realpath("data/temp")); + return false_throws(tempnam($temp, $prefix)); +} diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index 9bd29d89..58b11bbd 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -105,7 +105,7 @@ class AliasEditor extends Extension if ($user->can(Permissions::MANAGE_ALIAS_LIST)) { if (count($_FILES) > 0) { $tmp = $_FILES['alias_file']['tmp_name']; - $contents = false_throws(file_get_contents($tmp)); + $contents = file_get_contents_ex($tmp); $this->add_alias_csv($contents); log_info("alias_editor", "Imported aliases from file", "Imported aliases"); # FIXME: how many? $page->set_mode(PageMode::REDIRECT); diff --git a/ext/artists/main.php b/ext/artists/main.php index 2570a612..30303069 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -19,6 +19,12 @@ class AuthorSetEvent extends Event } } +/** + * @phpstan-type ArtistArtist array{id:int,artist_id:int,user_name:string,name:string,notes:string,type:string,posts:int} + * @phpstan-type ArtistAlias array{id:int,alias_id:int,alias_name:string,alias:string} + * @phpstan-type ArtistMember array{id:int,name:string} + * @phpstan-type ArtistUrl array{id:int,url:string} + */ class Artists extends Extension { /** @var ArtistsTheme */ @@ -514,7 +520,7 @@ class Artists extends Extension } /** - * @return array + * @return ArtistAlias */ private function get_alias_by_id(int $aliasID): array { @@ -523,7 +529,7 @@ class Artists extends Extension } /** - * @return array + * @return ArtistUrl */ private function get_url_by_id(int $urlID): array { @@ -532,7 +538,7 @@ class Artists extends Extension } /** - * @return array + * @return ArtistMember */ private function get_member_by_id(int $memberID): array { @@ -617,6 +623,7 @@ class Artists extends Extension // URLS MATCHING SECTION $i = 0; + assert(is_string($urlsAsString)); $urlsAsString = str_replace("\r\n", "\n", $urlsAsString); $urlsAsString = str_replace("\n\r", "\n", $urlsAsString); $urlsAsArray = empty($urlsAsString) ? [] : explode("\n", $urlsAsString); @@ -747,6 +754,7 @@ class Artists extends Extension } if (!is_null($urls)) { + assert(is_string($urls)); //delete double "separators" $urls = str_replace("\r\n", "\n", $urls); $urls = str_replace("\n\r", "\n", $urls); @@ -782,7 +790,7 @@ class Artists extends Extension } /** - * @return array + * @return ArtistArtist */ private function get_artist(int $artistID): array { @@ -799,7 +807,7 @@ class Artists extends Extension } /** - * @return array + * @return ArtistMember[] */ private function get_members(int $artistID): array { @@ -818,7 +826,7 @@ class Artists extends Extension } /** - * @return array + * @return ArtistUrl[] */ private function get_urls(int $artistID): array { diff --git a/ext/auto_tagger/main.php b/ext/auto_tagger/main.php index 1f3c3793..3d328991 100644 --- a/ext/auto_tagger/main.php +++ b/ext/auto_tagger/main.php @@ -111,7 +111,7 @@ class AutoTagger extends Extension if ($user->can(Permissions::MANAGE_AUTO_TAG)) { if (count($_FILES) > 0) { $tmp = $_FILES['auto_tag_file']['tmp_name']; - $contents = false_throws(file_get_contents($tmp)); + $contents = file_get_contents_ex($tmp); $count = $this->add_auto_tag_csv($contents); log_info(AutoTaggerInfo::KEY, "Imported $count auto-tag definitions from file from file", "Imported $count auto-tag definitions"); $page->set_mode(PageMode::REDIRECT); diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index 57571976..cec7170e 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -23,7 +23,7 @@ class AutoComplete extends Extension $page->set_mode(PageMode::DATA); $page->set_mime(MimeType::JSON); - $page->set_data(json_encode($res)); + $page->set_data(json_encode_ex($res)); } } diff --git a/ext/blotter/theme.php b/ext/blotter/theme.php index fb047b5a..6a9750e6 100644 --- a/ext/blotter/theme.php +++ b/ext/blotter/theme.php @@ -141,7 +141,7 @@ class BlotterTheme extends Themelet $i_close = ""; //$id = $entries[$i]['id']; $messy_date = $entries[$i]['entry_date']; - $clean_date = date("y/m/d", strtotime($messy_date)); + $clean_date = date("y/m/d", strtotime_ex($messy_date)); $entry_text = $entries[$i]['entry_text']; if ($entries[$i]['important'] == 'Y') { $i_open = ""; @@ -171,7 +171,7 @@ class BlotterTheme extends Themelet $i_close = ""; //$id = $entry['id']; $messy_date = $entry['entry_date']; - $clean_date = date("m/d/y", strtotime($messy_date)); + $clean_date = date("m/d/y", strtotime_ex($messy_date)); $entry_text = $entry['entry_text']; if ($entry['important'] == 'Y') { $i_open = ""; @@ -192,7 +192,7 @@ class BlotterTheme extends Themelet $out_text = "No blotter entries yet."; $in_text = "Empty."; } else { - $clean_date = date("m/d/y", strtotime($entries[0]['entry_date'])); + $clean_date = date("m/d/y", strtotime_ex($entries[0]['entry_date'])); $out_text = "Blotter updated: {$clean_date}"; $in_text = "
    $entries_list
"; } diff --git a/ext/browser_search/main.php b/ext/browser_search/main.php index fa5b8e9f..890e8b41 100644 --- a/ext/browser_search/main.php +++ b/ext/browser_search/main.php @@ -28,7 +28,7 @@ class BrowserSearch extends Extension $search_title = $config->get_string(SetupConfig::TITLE); $search_form_url = search_link(['{searchTerms}']); $suggenton_url = make_link('browser_search/')."{searchTerms}"; - $icon_b64 = base64_encode(false_throws(file_get_contents("ext/static_files/static/favicon.ico"))); + $icon_b64 = base64_encode(file_get_contents_ex("ext/static_files/static/favicon.ico")); // Now for the XML $xml = " @@ -71,7 +71,7 @@ class BrowserSearch extends Extension // And to do stuff with it. We want our output to look like: // ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]] $page->set_mode(PageMode::DATA); - $page->set_data(json_encode([$tag_search, $tags, [], []])); + $page->set_data(json_encode_ex([$tag_search, $tags, [], []])); } } diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index c1ce0984..94e93fcf 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -87,7 +87,7 @@ class BulkAddCSV extends Extension $linenum = 1; $list = ""; - $csvhandle = fopen($csvfile, "r"); + $csvhandle = false_throws(fopen($csvfile, "r")); while (($csvdata = fgetcsv($csvhandle, 0, ",")) !== false) { if (count($csvdata) != 5) { diff --git a/ext/bulk_download/main.php b/ext/bulk_download/main.php index d6e1bdf7..6d8210e2 100644 --- a/ext/bulk_download/main.php +++ b/ext/bulk_download/main.php @@ -48,7 +48,7 @@ class BulkDownload extends Extension if ($user->can(Permissions::BULK_DOWNLOAD) && ($event->action == BulkDownload::DOWNLOAD_ACTION_NAME)) { $download_filename = $user->name . '-' . date('YmdHis') . '.zip'; - $zip_filename = false_throws(tempnam(sys_get_temp_dir(), "shimmie_bulk_download")); + $zip_filename = shm_tempnam("bulk_download"); $zip = new \ZipArchive(); $size_total = 0; $max_size = $config->get_int(BulkDownloadConfig::SIZE_LIMIT); diff --git a/ext/bulk_import_export/main.php b/ext/bulk_import_export/main.php index 1b7f34fb..cd42422a 100644 --- a/ext/bulk_import_export/main.php +++ b/ext/bulk_import_export/main.php @@ -40,7 +40,7 @@ class BulkImportExport extends DataHandlerExtension continue; } - $tmpfile = tempnam(sys_get_temp_dir(), "shimmie_bulk_import"); + $tmpfile = shm_tempnam("bulk_import"); $stream = $zip->getStream($item->hash); if ($stream === false) { throw new SCoreException("Could not import " . $item->hash . ": File not in zip"); @@ -105,7 +105,7 @@ class BulkImportExport extends DataHandlerExtension if ($user->can(Permissions::BULK_EXPORT) && ($event->action == self::EXPORT_ACTION_NAME)) { $download_filename = $user->name . '-' . date('YmdHis') . '.zip'; - $zip_filename = tempnam(sys_get_temp_dir(), "shimmie_bulk_export"); + $zip_filename = shm_tempnam("bulk_export"); $zip = new \ZipArchive(); $json_data = []; @@ -126,7 +126,7 @@ class BulkImportExport extends DataHandlerExtension $zip->addFile($img_loc, $image->hash); } - $json_data = json_encode($json_data, JSON_PRETTY_PRINT); + $json_data = json_encode_ex($json_data, JSON_PRETTY_PRINT); $zip->addFromString(self::EXPORT_INFO_FILE_NAME, $json_data); $zip->close(); @@ -163,7 +163,7 @@ class BulkImportExport extends DataHandlerExtension $info = $zip->getStream(self::EXPORT_INFO_FILE_NAME); if ($info !== false) { try { - $json_string = stream_get_contents($info); + $json_string = false_throws(stream_get_contents($info)); $json_data = json_decode($json_string); return $json_data; } finally { diff --git a/ext/comment/main.php b/ext/comment/main.php index 5d05ead2..7ad5f1c1 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -311,7 +311,7 @@ class CommentList extends Extension LIMIT :limit OFFSET :offset ", ["limit" => $threads_per_page, "offset" => $start]); - $user_ratings = Extension::is_enabled(RatingsInfo::KEY) ? Ratings::get_user_class_privs($user) : ""; + $user_ratings = Extension::is_enabled(RatingsInfo::KEY) ? Ratings::get_user_class_privs($user) : []; $images = []; while ($row = $result->fetch()) { @@ -369,7 +369,7 @@ class CommentList extends Extension public function onUserPageBuilding(UserPageBuildingEvent $event): void { - $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $i_days_old = ((time() - strtotime_ex($event->display_user->join_date)) / 86400) + 1; $i_comment_count = Comment::count_comments_by_user($event->display_user); $h_comment_rate = sprintf("%.1f", ($i_comment_count / $i_days_old)); $event->add_stats("Comments made: $i_comment_count, $h_comment_rate per day"); @@ -651,7 +651,7 @@ class CommentList extends Extension } // advanced sanity checks - elseif (strlen($comment) / strlen(gzcompress($comment)) > 10) { + elseif (strlen($comment) / strlen(false_throws(gzcompress($comment))) > 10) { throw new CommentPostingException("Comment too repetitive~"); } elseif ($user->is_anonymous() && !$this->hash_match()) { $page->add_cookie("nocache", "Anonymous Commenter", time() + 60 * 60 * 24, "/"); diff --git a/ext/comment/theme.php b/ext/comment/theme.php index a79bd759..bfd0dfe0 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -59,7 +59,7 @@ class CommentListTheme extends Themelet $comment_count = count($comments); if ($comment_limit > 0 && $comment_count > $comment_limit) { $comment_html .= "

showing $comment_limit of $comment_count comments

"; - $comments = array_slice($comments, -$comment_limit); + $comments = array_slice($comments, negative_int($comment_limit)); $this->show_anon_id = false; } else { $this->show_anon_id = true; @@ -255,7 +255,7 @@ class CommentListTheme extends Themelet $h_del = ""; if ($user->can(Permissions::DELETE_COMMENT)) { $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $j_delete_confirm_message = json_encode_ex("Delete comment by {$comment->owner_name}:\n$comment_preview"); $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); $h_del = " - Del"; diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 201bf0c7..e2da4392 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -235,7 +235,7 @@ class CronUploader extends Extension $running = false; - $lockfile = fopen($this->get_lock_file(), "w"); + $lockfile = false_throws(fopen($this->get_lock_file(), "w")); try { if (!flock($lockfile, LOCK_EX | LOCK_NB)) { $running = true; @@ -343,7 +343,7 @@ class CronUploader extends Extension throw new SCoreException("User does not have permission to run cron upload"); } - $lockfile = fopen($this->get_lock_file(), "w"); + $lockfile = false_throws(fopen($this->get_lock_file(), "w")); if (!flock($lockfile, LOCK_EX | LOCK_NB)) { throw new SCoreException("Cron upload process is already running"); } diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index 980ff2ee..6d515ce4 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -311,7 +311,7 @@ class DanbooruApi extends Extension } } elseif (isset($_REQUEST['source']) || isset($_REQUEST['post']['source'])) { // A url was provided $source = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source']; - $file = tempnam(sys_get_temp_dir(), "shimmie_transload"); + $file = shm_tempnam("transload"); assert($file !== false); try { fetch_url($source, $file); diff --git a/ext/emoticons_list/main.php b/ext/emoticons_list/main.php index b66e0649..a0dd7271 100644 --- a/ext/emoticons_list/main.php +++ b/ext/emoticons_list/main.php @@ -15,7 +15,7 @@ class EmoticonList extends Extension public function onPageRequest(PageRequestEvent $event): void { if ($event->page_matches("emote/list")) { - $this->theme->display_emotes(glob("ext/emoticons/default/*")); + $this->theme->display_emotes(glob_ex("ext/emoticons/default/*")); } } } diff --git a/ext/eokm/main.php b/ext/eokm/main.php index 1bc13942..dd1e9f52 100644 --- a/ext/eokm/main.php +++ b/ext/eokm/main.php @@ -18,7 +18,7 @@ class Eokm extends Extension $password = $config->get_string("eokm_password"); if ($username && $password) { - $ch = curl_init("https://api.eokmhashdb.nl/v1/check/md5"); + $ch = false_throws(curl_init("https://api.eokmhashdb.nl/v1/check/md5")); // curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml', $additionalHeaders)); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password); diff --git a/ext/et/main.php b/ext/et/main.php index 33db0109..d56bfbb6 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -120,9 +120,9 @@ class ET extends Extension if (file_exists(".git")) { try { - $commitHash = trim(exec('git log --pretty="%h" -n1 HEAD')); - $commitBranch = trim(exec('git rev-parse --abbrev-ref HEAD')); - $commitOrigin = trim(exec('git config --get remote.origin.url')); + $commitHash = trim(exec_ex('git log --pretty="%h" -n1 HEAD')); + $commitBranch = trim(exec_ex('git rev-parse --abbrev-ref HEAD')); + $commitOrigin = trim(exec_ex('git config --get remote.origin.url')); $commitOrigin = preg_replace("#//.*@#", "//xxx@", $commitOrigin); $info['git'] = [ 'commit' => $commitHash, @@ -146,7 +146,7 @@ class ET extends Extension foreach ($info as $title => $section) { $data .= "$title:\n"; foreach ($section as $k => $v) { - $data .= " $k: " . json_encode($v, JSON_UNESCAPED_SLASHES) . "\n"; + $data .= " $k: " . json_encode_ex($v, JSON_UNESCAPED_SLASHES) . "\n"; } $data .= "\n"; } diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 8b83e0ad..d5ad9306 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -78,7 +78,7 @@ class Favorites extends Extension public function onUserPageBuilding(UserPageBuildingEvent $event): void { $i_favorites_count = Search::count_images(["favorited_by={$event->display_user->name}"]); - $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $i_days_old = ((time() - strtotime_ex($event->display_user->join_date)) / 86400) + 1; $h_favorites_rate = sprintf("%.1f", ($i_favorites_count / $i_days_old)); $favorites_link = search_link(["favorited_by={$event->display_user->name}"]); $event->add_stats("Posts favorited: $i_favorites_count, $h_favorites_rate per day"); diff --git a/ext/featured/main.php b/ext/featured/main.php index 86cbab67..e0d05f0b 100644 --- a/ext/featured/main.php +++ b/ext/featured/main.php @@ -35,7 +35,7 @@ class Featured extends Extension if (!is_null($image)) { $page->set_mode(PageMode::DATA); $page->set_mime($image->get_mime()); - $page->set_data(file_get_contents($image->get_image_filename())); + $page->set_data(file_get_contents_ex($image->get_image_filename())); } } if ($event->get_arg(0) == "view") { diff --git a/ext/forum/main.php b/ext/forum/main.php index 4c737c26..176f1595 100644 --- a/ext/forum/main.php +++ b/ext/forum/main.php @@ -82,7 +82,7 @@ class Forum extends Extension $threads_count = $database->get_one("SELECT COUNT(*) FROM forum_threads WHERE user_id=:user_id", ['user_id' => $event->display_user->id]); $posts_count = $database->get_one("SELECT COUNT(*) FROM forum_posts WHERE user_id=:user_id", ['user_id' => $event->display_user->id]); - $days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $days_old = ((time() - strtotime_ex($event->display_user->join_date)) / 86400) + 1; $threads_rate = sprintf("%.1f", ($threads_count / $days_old)); $posts_rate = sprintf("%.1f", ($posts_count / $days_old)); diff --git a/ext/graphql/main.php b/ext/graphql/main.php index 17e92070..48de5587 100644 --- a/ext/graphql/main.php +++ b/ext/graphql/main.php @@ -92,6 +92,8 @@ class GraphQL extends Extension ]); $t2 = ftime(); $resp = $server->executeRequest(); + assert(!is_array($resp)); + assert(is_a($resp, \GraphQL\Executor\ExecutionResult::class)); if ($config->get_bool("graphql_debug")) { $debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS; $body = $resp->toArray($debug); @@ -105,13 +107,13 @@ class GraphQL extends Extension // sleep(1); $page->set_mode(PageMode::DATA); $page->set_mime("application/json"); - $page->set_data(\json_encode($body, JSON_UNESCAPED_UNICODE)); + $page->set_data(json_encode_ex($body, JSON_UNESCAPED_UNICODE)); } if ($event->page_matches("graphql_upload")) { $this->cors(); $page->set_mode(PageMode::DATA); $page->set_mime("application/json"); - $page->set_data(\json_encode(self::handle_uploads())); + $page->set_data(json_encode_ex(self::handle_uploads())); } } @@ -200,7 +202,7 @@ class GraphQL extends Extension $body['stats'] = get_debug_info_arr(); $body['stats']['graphql_schema_time'] = round($t2 - $t1, 2); $body['stats']['graphql_execute_time'] = round($t3 - $t2, 2); - echo \json_encode($body, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + echo json_encode_ex($body, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); return Command::SUCCESS; }); $event->app->register('graphql:schema') diff --git a/ext/handle_archive/main.php b/ext/handle_archive/main.php index 85851ddb..5d1fa85a 100644 --- a/ext/handle_archive/main.php +++ b/ext/handle_archive/main.php @@ -31,6 +31,7 @@ class ArchiveFileHandler extends DataHandlerExtension $cmd = $config->get_string('archive_extract_command'); $cmd = str_replace('%f', $event->tmpname, $cmd); $cmd = str_replace('%d', $tmpdir, $cmd); + assert(is_string($cmd)); exec($cmd); if (file_exists($tmpdir)) { try { diff --git a/ext/handle_cbz/main.php b/ext/handle_cbz/main.php index 5c035151..c160d52b 100644 --- a/ext/handle_cbz/main.php +++ b/ext/handle_cbz/main.php @@ -39,7 +39,7 @@ class CBZFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - $fp = fopen($tmpname, "r"); + $fp = false_throws(fopen($tmpname, "r")); $head = fread($fp, 4); fclose($fp); return $head == "PK\x03\x04"; @@ -53,7 +53,7 @@ class CBZFileHandler extends DataHandlerExtension $za->open($archive); $names = []; for ($i = 0; $i < $za->numFiles;$i++) { - $file = $za->statIndex($i); + $file = false_throws($za->statIndex($i)); $names[] = $file['name']; } sort($names); diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index cecb30d5..145857a7 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -15,10 +15,10 @@ class IcoFileHandler extends DataHandlerExtension $event->image->audio = false; $event->image->image = ($event->image->get_mime() != MimeType::ANI); - $fp = fopen($event->image->get_image_filename(), "r"); + $fp = false_throws(fopen($event->image->get_image_filename(), "r")); try { fseek($fp, 6); // skip header - $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); + $subheader = false_throws(unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", false_throws(fread($fp, 16)))); $width = $subheader['width']; $height = $subheader['height']; $event->image->width = $width == 0 ? 256 : $width; @@ -41,8 +41,8 @@ class IcoFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - $fp = fopen($tmpname, "r"); - $header = unpack("Snull/Stype/Scount", fread($fp, 6)); + $fp = false_throws(fopen($tmpname, "r")); + $header = false_throws(unpack("Snull/Stype/Scount", false_throws(fread($fp, 6)))); fclose($fp); return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); } diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 9d9e7120..1eff8776 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -26,7 +26,7 @@ class SVGFileHandler extends DataHandlerExtension $sanitizer = new Sanitizer(); $sanitizer->removeRemoteReferences(true); - $dirtySVG = file_get_contents(warehouse_path(Image::IMAGE_DIR, $hash)); + $dirtySVG = file_get_contents_ex(warehouse_path(Image::IMAGE_DIR, $hash)); $cleanSVG = $sanitizer->sanitize($dirtySVG); $page->set_data($cleanSVG); } @@ -41,10 +41,10 @@ class SVGFileHandler extends DataHandlerExtension // then sanitise it before touching it $sanitizer = new Sanitizer(); $sanitizer->removeRemoteReferences(true); - $dirtySVG = file_get_contents($event->tmpname); - $cleanSVG = $sanitizer->sanitize($dirtySVG); + $dirtySVG = file_get_contents_ex($event->tmpname); + $cleanSVG = false_throws($sanitizer->sanitize($dirtySVG)); $event->hash = md5($cleanSVG); - $new_tmpname = tempnam(sys_get_temp_dir(), "shimmie_svg"); + $new_tmpname = shm_tempnam("svg"); file_put_contents($new_tmpname, $cleanSVG); $event->set_tmpname($new_tmpname); @@ -103,7 +103,7 @@ class MiniSVGParser { $xml_parser = xml_parser_create(); xml_set_element_handler($xml_parser, [$this, "startElement"], [$this, "endElement"]); - $this->valid = bool_escape(xml_parse($xml_parser, file_get_contents($file), true)); + $this->valid = bool_escape(xml_parse($xml_parser, file_get_contents_ex($file), true)); xml_parser_free($xml_parser); } diff --git a/ext/help_pages/main.php b/ext/help_pages/main.php index 5eb5e1ac..e63f5c67 100644 --- a/ext/help_pages/main.php +++ b/ext/help_pages/main.php @@ -18,7 +18,7 @@ class HelpPageListBuildingEvent extends Event class HelpPageBuildingEvent extends Event { public string $key; - /** @var array> */ + /** @var array */ public array $blocks = []; public function __construct(string $key) @@ -29,10 +29,10 @@ class HelpPageBuildingEvent extends Event public function add_block(Block $block, int $position = 50): void { - if (!array_key_exists("$position", $this->blocks)) { - $this->blocks["$position"] = []; + while (array_key_exists($position, $this->blocks)) { + $position++; } - $this->blocks["$position"][] = $block; + $this->blocks[$position] = $block; } } @@ -65,12 +65,9 @@ class HelpPages extends Extension $this->theme->display_help_page($title); $hpbe = send_event(new HelpPageBuildingEvent($name)); - asort($hpbe->blocks); - - foreach ($hpbe->blocks as $key => $value) { - foreach ($value as $block) { - $page->add_block($block); - } + ksort($hpbe->blocks); + foreach ($hpbe->blocks as $block) { + $page->add_block($block); } } } diff --git a/ext/home/main.php b/ext/home/main.php index d05c7b6d..7731205d 100644 --- a/ext/home/main.php +++ b/ext/home/main.php @@ -28,7 +28,7 @@ class Home extends Extension $counters = []; $counters["None"] = "none"; $counters["Text-only"] = "text-only"; - foreach (false_throws(glob("ext/home/counters/*")) as $counter_dirname) { + foreach (glob_ex("ext/home/counters/*") as $counter_dirname) { $name = str_replace("ext/home/counters/", "", $counter_dirname); $counters[ucfirst($name)] = $name; } diff --git a/ext/image/main.php b/ext/image/main.php index ec6e4676..c5b9285f 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -150,7 +150,7 @@ class ImageIO extends Extension { $u_name = url_escape($event->display_user->name); $i_image_count = Search::count_images(["user={$event->display_user->name}"]); - $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $i_days_old = ((time() - strtotime_ex($event->display_user->join_date)) / 86400) + 1; $h_image_rate = sprintf("%.1f", ($i_image_count / $i_days_old)); $images_link = search_link(["user=$u_name"]); $event->add_stats("Posts uploaded: $i_image_count, $h_image_rate per day"); @@ -202,7 +202,7 @@ class ImageIO extends Extension public function onParseLinkTemplate(ParseLinkTemplateEvent $event): void { $fname = $event->image->get_filename(); - $base_fname = str_contains($fname, '.') ? substr($fname, 0, strrpos($fname, '.')) : $fname; + $base_fname = basename($fname, '.' . $event->image->get_ext()); $event->replace('$id', (string)$event->image->id); $event->replace('$hash_ab', substr($event->image->hash, 0, 2)); @@ -241,7 +241,7 @@ class ImageIO extends Extension } else { $if_modified_since = ""; } - $gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT'; + $gmdate_mod = gmdate('D, d M Y H:i:s', false_throws(filemtime($file))) . ' GMT'; if ($if_modified_since == $gmdate_mod) { $page->set_mode(PageMode::DATA); diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 25dd8f97..2978fb3b 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -142,7 +142,7 @@ class IPBan extends Extension $row_banner_id_int = intval($row['banner_id']); - $msg = $config->get_string("ipban_message_{$row['mode']}") ?? $config->get_string("ipban_message"); + $msg = $config->get_string("ipban_message_{$row['mode']}") ?? $config->get_string("ipban_message") ?? "(no message)"; $msg = str_replace('$IP', $row["ip"], $msg); $msg = str_replace('$DATE', $row['expires'] ?? 'the end of time', $msg); $msg = str_replace('$ADMIN', User::by_id($row_banner_id_int)->name, $msg); @@ -153,6 +153,7 @@ class IPBan extends Extension } else { $msg = str_replace('$CONTACT', "", $msg); } + assert(is_string($msg)); $msg .= ""; if ($row["mode"] == "ghost") { diff --git a/ext/link_image/main.php b/ext/link_image/main.php index d1b41bba..297f62ff 100644 --- a/ext/link_image/main.php +++ b/ext/link_image/main.php @@ -28,7 +28,7 @@ class LinkImage extends Extension } /** - * @return array{thumb_src: string, image_src: string, post_link: string, text_link: string} + * @return array{thumb_src: string, image_src: string, post_link: string, text_link: string|null} */ private function data(Image $image): array { diff --git a/ext/link_image/theme.php b/ext/link_image/theme.php index cffc2278..1305df4e 100644 --- a/ext/link_image/theme.php +++ b/ext/link_image/theme.php @@ -7,7 +7,7 @@ namespace Shimmie2; class LinkImageTheme extends Themelet { /** - * @param array{thumb_src:string,image_src:string,post_link:string,text_link:string} $data + * @param array{thumb_src:string,image_src:string,post_link:string,text_link:string|null} $data */ public function links_block(Page $page, array $data): void { diff --git a/ext/log_db/main.php b/ext/log_db/main.php index 5831d111..1873465a 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -79,10 +79,11 @@ class ActorColumn extends Column } /** - * @return array{0: string, 1: string} + * @return array{0: string|null, 1: string|null} */ public function modify_input_for_read(string|array $input): array { + assert(is_array($input)); list($un, $ip) = $input; if (empty($un)) { $un = null; @@ -160,6 +161,7 @@ class MessageColumn extends Column public function modify_input_for_read(array|string $input): mixed { + assert(is_array($input)); list($m, $l) = $input; if (empty($m)) { $m = "%"; diff --git a/ext/log_logstash/main.php b/ext/log_logstash/main.php index a3fb8a75..33cc2c69 100644 --- a/ext/log_logstash/main.php +++ b/ext/log_logstash/main.php @@ -59,7 +59,7 @@ class LogLogstash extends Extension if (!$fp) { return; } - fwrite($fp, json_encode($data)); + fwrite($fp, json_encode_ex($data)); fclose($fp); } catch (\Exception $e) { // we can't log that logging is broken diff --git a/ext/media/main.php b/ext/media/main.php index 9834ad24..3d57a9e1 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -332,7 +332,7 @@ class Media extends Extension $ok = false; $inname = $image->get_image_filename(); - $tmpname = tempnam(sys_get_temp_dir(), "shimmie_ffmpeg_thumb"); + $tmpname = shm_tempnam("ffmpeg_thumb"); try { $outname = $image->get_thumb_filename(); @@ -702,16 +702,17 @@ class Media extends Extension $new_width = $width; } - $image = imagecreatefromstring(file_get_contents($image_filename)); - $image_resized = imagecreatetruecolor($new_width, $new_height); - try { - if ($image === false) { - throw new MediaException("Could not load image: " . $image_filename); - } - if ($image_resized === false) { - throw new MediaException("Could not create output image with dimensions $new_width c $new_height "); - } + $image = imagecreatefromstring(file_get_contents_ex($image_filename)); + if ($image === false) { + throw new MediaException("Could not load image: " . $image_filename); + } + $image_resized = imagecreatetruecolor($new_width, $new_height); + if ($image_resized === false) { + throw new MediaException("Could not create output image with dimensions $new_width x $new_height "); + } + + try { // Handle transparent images switch ($info[2]) { case IMAGETYPE_GIF: @@ -849,7 +850,7 @@ class Media extends Extension "-y", "-i", escapeshellarg($filename), "-vstats" ])); - $output = shell_exec($cmd . " 2>&1"); + $output = null_throws(false_throws(shell_exec($cmd . " 2>&1"))); // error_log("Getting size with `$cmd`"); $regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/"; @@ -941,9 +942,9 @@ class Media extends Extension public static function hex_color_allocate(mixed $im, string $hex): int { $hex = ltrim($hex, '#'); - $a = hexdec(substr($hex, 0, 2)); - $b = hexdec(substr($hex, 2, 2)); - $c = hexdec(substr($hex, 4, 2)); + $a = (int)hexdec(substr($hex, 0, 2)); + $b = (int)hexdec(substr($hex, 2, 2)); + $c = (int)hexdec(substr($hex, 4, 2)); $col = imagecolorallocate($im, $a, $b, $c); assert($col !== false); return $col; diff --git a/ext/mime/mime_type.php b/ext/mime/mime_type.php index 1c8af44b..2ac38208 100644 --- a/ext/mime/mime_type.php +++ b/ext/mime/mime_type.php @@ -159,7 +159,7 @@ class MimeType */ private static function compare_file_bytes(string $file_name, array $comparison): bool { - $size = filesize($file_name); + $size = filesize_ex($file_name); $cc = count($comparison); if ($size < $cc) { // Can't match because it's too small @@ -168,7 +168,7 @@ class MimeType if (($fh = @fopen($file_name, 'rb'))) { try { - $chunk = unpack("C*", fread($fh, $cc)); + $chunk = false_throws(unpack("C*", false_throws(fread($fh, $cc)))); for ($i = 0; $i < $cc; $i++) { $byte = $comparison[$i]; @@ -231,12 +231,9 @@ class MimeType $output = self::OCTET_STREAM; - $finfo = finfo_open(FILEINFO_MIME_TYPE); - try { - $type = finfo_file($finfo, $file); - } finally { - finfo_close($finfo); - } + $finfo = false_throws(finfo_open(FILEINFO_MIME_TYPE)); + $type = finfo_file($finfo, $file); + finfo_close($finfo); if ($type !== false && !empty($type)) { $output = $type; diff --git a/ext/notes/main.php b/ext/notes/main.php index 905af893..a495b006 100644 --- a/ext/notes/main.php +++ b/ext/notes/main.php @@ -126,7 +126,7 @@ class Notes extends Extension $page->set_mode(PageMode::DATA); if (!$user->is_anonymous()) { $note_id = $this->add_new_note(); - $page->set_data(json_encode([ + $page->set_data(json_encode_ex([ 'status' => 'success', 'note_id' => $note_id, ])); @@ -136,14 +136,14 @@ class Notes extends Extension $page->set_mode(PageMode::DATA); if (!$user->is_anonymous()) { $this->update_note(); - $page->set_data(json_encode(['status' => 'success'])); + $page->set_data(json_encode_ex(['status' => 'success'])); } break; case "delete_note": $page->set_mode(PageMode::DATA); if ($user->can(Permissions::NOTES_ADMIN)) { $this->delete_note(); - $page->set_data(json_encode(['status' => 'success'])); + $page->set_data(json_encode_ex(['status' => 'success'])); } break; case "nuke_notes": @@ -256,7 +256,7 @@ class Notes extends Extension { global $database, $user; - $note = json_decode(file_get_contents('php://input'), true); + $note = json_decode(file_get_contents_ex('php://input'), true); $database->execute( " @@ -318,7 +318,7 @@ class Notes extends Extension { global $database; - $note = json_decode(file_get_contents('php://input'), true); + $note = json_decode(file_get_contents_ex('php://input'), true); // validate parameters if (empty($note['note'])) { @@ -337,7 +337,7 @@ class Notes extends Extension { global $user, $database; - $note = json_decode(file_get_contents('php://input'), true); + $note = json_decode(file_get_contents_ex('php://input'), true); $database->execute(" UPDATE notes SET enable = :enable WHERE image_id = :image_id AND id = :id @@ -369,11 +369,11 @@ class Notes extends Extension global $database, $config; $pageNumber = $event->try_page_num(1); - $notesPerPage = $config->get_int('notesNotesPerPage'); + $totalPages = (int)ceil($database->get_one("SELECT COUNT(DISTINCT image_id) FROM notes") / $notesPerPage); //$result = $database->get_all("SELECT * FROM pool_images WHERE pool_id=:pool_id", ['pool_id'=>$poolID]); - $result = $database->execute( + $image_ids = $database->get_col( " SELECT DISTINCT image_id FROM notes @@ -382,11 +382,9 @@ class Notes extends Extension ['enable' => 1, 'offset' => $pageNumber * $notesPerPage, 'limit' => $notesPerPage] ); - $totalPages = (int)ceil($database->get_one("SELECT COUNT(DISTINCT image_id) FROM notes") / $notesPerPage); - $images = []; - while ($row = $result->fetch()) { - $images[] = [Image::by_id($row["image_id"])]; + foreach($image_ids as $id) { + $images[] = Image::by_id($id); } $this->theme->display_note_list($images, $pageNumber + 1, $totalPages); @@ -414,7 +412,7 @@ class Notes extends Extension $images = []; while ($row = $result->fetch()) { - $images[] = [Image::by_id($row["image_id"])]; + $images[] = Image::by_id($row["image_id"]); } $this->theme->display_note_requests($images, $pageNumber + 1, $totalPages); diff --git a/ext/notes/theme.php b/ext/notes/theme.php index b8adf249..58495d6c 100644 --- a/ext/notes/theme.php +++ b/ext/notes/theme.php @@ -74,22 +74,20 @@ class NotesTheme extends Themelet ]; } $page->add_html_header(""); } /** - * @param array $images + * @param array $images */ public function display_note_list(array $images, int $pageNumber, int $totalPages): void { global $page; $pool_images = ''; - foreach ($images as $pair) { - $image = $pair[0]; - + foreach ($images as $image) { $thumb_html = $this->build_thumb_html($image); $pool_images .= ''. @@ -104,18 +102,15 @@ class NotesTheme extends Themelet } /** - * @param array $images + * @param array $images */ public function display_note_requests(array $images, int $pageNumber, int $totalPages): void { global $page; $pool_images = ''; - foreach ($images as $pair) { - $image = $pair[0]; - + foreach ($images as $image) { $thumb_html = $this->build_thumb_html($image); - $pool_images .= ''. ' '.$thumb_html.''. ''; diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index cb6939ec..35c6005c 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -205,25 +205,25 @@ class NumericScore extends Extension $totaldate = $year."/".$month."/".$day; - $sql = "SELECT id FROM images - WHERE EXTRACT(YEAR FROM posted) = :year - "; + $sql = "SELECT id FROM images WHERE EXTRACT(YEAR FROM posted) = :year"; $args = ["limit" => $config->get_int(IndexConfig::IMAGES), "year" => $year]; if ($event->page_matches("popular_by_day")) { - $sql .= - "AND EXTRACT(MONTH FROM posted) = :month - AND EXTRACT(DAY FROM posted) = :day"; - + $sql .= " AND EXTRACT(MONTH FROM posted) = :month AND EXTRACT(DAY FROM posted) = :day"; $args = array_merge($args, ["month" => $month, "day" => $day]); - $dte = [$totaldate, date("F jS, Y", (strtotime($totaldate))), "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m\\&\\d\\a\\y\\=d", "day"]; + $current = date("F jS, Y", strtotime_ex($totaldate)). + $name = "day"; + $fmt = "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m\\&\\d\\a\\y\\=d"; } elseif ($event->page_matches("popular_by_month")) { - $sql .= "AND EXTRACT(MONTH FROM posted) = :month"; - + $sql .= " AND EXTRACT(MONTH FROM posted) = :month"; $args = array_merge($args, ["month" => $month]); - $dte = [$totaldate, date("F Y", (strtotime($totaldate))), "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m", "month"]; + $current = date("F Y", strtotime_ex($totaldate)); + $name = "month"; + $fmt = "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m"; } elseif ($event->page_matches("popular_by_year")) { - $dte = [$totaldate, $year, "\\y\\e\\a\\r\=Y", "year"]; + $current = "$year"; + $name = "year"; + $fmt = "\\y\\e\\a\\r\=Y"; } else { // this should never happen due to the fact that the page event is already matched against earlier. throw new \UnexpectedValueException("Error: Invalid page event."); @@ -234,7 +234,7 @@ class NumericScore extends Extension $ids = $database->get_col($sql, $args); $images = Search::get_images($ids); - $this->theme->view_popular($images, $dte); + $this->theme->view_popular($images, $totaldate, $current, $name, $fmt); } } diff --git a/ext/numeric_score/theme.php b/ext/numeric_score/theme.php index 318978aa..64e94e54 100644 --- a/ext/numeric_score/theme.php +++ b/ext/numeric_score/theme.php @@ -70,9 +70,8 @@ class NumericScoreTheme extends Themelet /** * @param Image[] $images - * @param string[] $dte */ - public function view_popular(array $images, array $dte): void + public function view_popular(array $images, string $totaldate, string $current, string $name, string $fmt): void { global $page, $config; @@ -81,12 +80,12 @@ class NumericScoreTheme extends Themelet $pop_images .= $this->build_thumb_html($image)."\n"; } - $b_dte = make_link("popular_by_".$dte[3], date($dte[2], (strtotime('-1 '.$dte[3], strtotime($dte[0]))))); - $f_dte = make_link("popular_by_".$dte[3], date($dte[2], (strtotime('+1 '.$dte[3], strtotime($dte[0]))))); + $b_dte = make_link("popular_by_$name", date($fmt, strtotime_ex("-1 $name", strtotime_ex($totaldate)))); + $f_dte = make_link("popular_by_$name", date($fmt, strtotime_ex("+1 $name", strtotime_ex($totaldate)))); $html = "\n". "

\n". - " « {$dte[1]} »\n". + " « {$current} »\n". "

\n". "
\n".$pop_images; diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index 422ef592..4bb79374 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -69,7 +69,7 @@ class _SafeOuroborosImage // meta $this->change = intval($img->id); //DaFug is this even supposed to do? ChangeID? // Should be JSON specific, just strip this when converting to XML - $this->created_at = ['n' => 123456789, 's' => strtotime($img->posted), 'json_class' => 'Time']; + $this->created_at = ['n' => 123456789, 's' => strtotime_ex($img->posted), 'json_class' => 'Time']; $this->id = intval($img->id); $this->parent_id = null; @@ -139,19 +139,19 @@ class OuroborosPost extends _SafeOuroborosImage $this->rating = $post['rating']; } if (array_key_exists('source', $post)) { - $this->file_url = filter_var( + $this->file_url = filter_var_ex( urldecode($post['source']), FILTER_SANITIZE_URL ); } if (array_key_exists('sourceurl', $post)) { - $this->source = filter_var( + $this->source = filter_var_ex( urldecode($post['sourceurl']), FILTER_SANITIZE_URL ); } if (array_key_exists('description', $post)) { - $this->description = filter_var( + $this->description = filter_var_ex( $post['description'], FILTER_SANITIZE_STRING ); @@ -260,7 +260,7 @@ class OuroborosAPI extends Extension if ($this->match('create')) { // Create if ($user->can(Permissions::CREATE_IMAGE)) { - $md5 = !empty($_REQUEST['md5']) ? filter_var($_REQUEST['md5'], FILTER_SANITIZE_STRING) : null; + $md5 = !empty($_REQUEST['md5']) ? filter_var_ex($_REQUEST['md5'], FILTER_SANITIZE_STRING) : null; $this->postCreate(new OuroborosPost($_REQUEST['post']), $md5); } else { $this->sendResponse(403, 'You cannot create new posts'); @@ -269,17 +269,17 @@ class OuroborosAPI extends Extension throw new SCoreException("update not implemented"); } elseif ($this->match('show')) { // Show - $id = !empty($_REQUEST['id']) ? (int)filter_var($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) : null; + $id = !empty($_REQUEST['id']) ? (int)filter_var_ex($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) : null; $this->postShow($id); } elseif ($this->match('index') || $this->match('list')) { // List $limit = !empty($_REQUEST['limit']) ? intval( - filter_var($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT) + filter_var_ex($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT) ) : 45; $p = !empty($_REQUEST['page']) ? intval( - filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) + filter_var_ex($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) ) : 1; - $tags = !empty($_REQUEST['tags']) ? filter_var($_REQUEST['tags'], FILTER_SANITIZE_STRING) : []; + $tags = !empty($_REQUEST['tags']) ? filter_var_ex($_REQUEST['tags'], FILTER_SANITIZE_STRING) : []; if (is_string($tags)) { $tags = Tag::explode($tags); } @@ -288,23 +288,23 @@ class OuroborosAPI extends Extension } elseif ($event->page_matches('tag')) { if ($this->match('index') || $this->match('list')) { $limit = !empty($_REQUEST['limit']) ? intval( - filter_var($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT) + filter_var_ex($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT) ) : 50; $p = !empty($_REQUEST['page']) ? intval( - filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) + filter_var_ex($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) ) : 1; - $order = (!empty($_REQUEST['order']) && ($_REQUEST['order'] == 'date' || $_REQUEST['order'] == 'count' || $_REQUEST['order'] == 'name')) ? filter_var( + $order = (!empty($_REQUEST['order']) && ($_REQUEST['order'] == 'date' || $_REQUEST['order'] == 'count' || $_REQUEST['order'] == 'name')) ? filter_var_ex( $_REQUEST['order'], FILTER_SANITIZE_STRING ) : 'date'; $id = !empty($_REQUEST['id']) ? intval( - filter_var($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) + filter_var_ex($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) ) : null; $after_id = !empty($_REQUEST['after_id']) ? intval( - filter_var($_REQUEST['after_id'], FILTER_SANITIZE_NUMBER_INT) + filter_var_ex($_REQUEST['after_id'], FILTER_SANITIZE_NUMBER_INT) ) : null; - $name = !empty($_REQUEST['name']) ? filter_var($_REQUEST['name'], FILTER_SANITIZE_STRING) : ''; - $name_pattern = !empty($_REQUEST['name_pattern']) ? filter_var( + $name = !empty($_REQUEST['name']) ? filter_var_ex($_REQUEST['name'], FILTER_SANITIZE_STRING) : ''; + $name_pattern = !empty($_REQUEST['name_pattern']) ? filter_var_ex( $_REQUEST['name_pattern'], FILTER_SANITIZE_STRING ) : ''; @@ -344,13 +344,13 @@ class OuroborosAPI extends Extension $meta['rating'] = $post->rating; } // Check where we should try for the file - if (empty($post->file) && !empty($post->file_url) && filter_var( + if (empty($post->file) && !empty($post->file_url) && filter_var_ex( $post->file_url, FILTER_VALIDATE_URL ) !== false ) { // Transload from source - $meta['file'] = tempnam(sys_get_temp_dir(), 'shimmie_transload_' . $config->get_string(UploadConfig::TRANSLOAD_ENGINE)); + $meta['file'] = shm_tempnam('transload_' . $config->get_string(UploadConfig::TRANSLOAD_ENGINE)); $meta['filename'] = basename($post->file_url); try { fetch_url($post->file_url, $meta['file']); @@ -459,20 +459,17 @@ class OuroborosAPI extends Extension default: $tag_data = $database->get_all( " - SELECT id, tag, count - FROM tags - WHERE count >= :tags_min - ORDER BY count DESC, tag ASC LIMIT :start, :max_items - ", + SELECT id, tag, count + FROM tags + WHERE count >= :tags_min + ORDER BY count DESC, tag ASC LIMIT :start, :max_items + ", ['tags_min' => $config->get_int(TagListConfig::TAGS_MIN), 'start' => $start, 'max_items' => $limit] ); break; } $tags = []; foreach ($tag_data as $tag) { - if (!is_array($tag)) { - continue; - } $tags[] = new _SafeOuroborosTag($tag); } $this->sendData('tag', $tags, $start); @@ -517,7 +514,7 @@ class OuroborosAPI extends Extension $response['location'] = $response['reason']; unset($response['reason']); } - $response = json_encode($response); + $response = json_encode_ex($response); } elseif ($this->type == 'xml') { // Seriously, XML sucks... $xml = new \XMLWriter(); @@ -546,7 +543,7 @@ class OuroborosAPI extends Extension global $page; $response = ''; if ($this->type == 'json') { - $response = json_encode($data); + $response = json_encode_ex($data); } elseif ($this->type == 'xml') { $xml = new \XMLWriter(); $xml->openMemory(); @@ -575,7 +572,7 @@ class OuroborosAPI extends Extension private function createItemXML(\XMLWriter $xml, string $type, _SafeOuroborosTag|_SafeOuroborosImage $item): void { $xml->startElement($type); - foreach (json_decode(json_encode($item)) as $key => $val) { + foreach (json_decode(json_encode_ex($item)) as $key => $val) { if ($key == 'created_at' && $type == 'post') { $xml->writeAttribute($key, $val['s']); } else { diff --git a/ext/pools/main.php b/ext/pools/main.php index 22f4b973..6a556676 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -250,15 +250,6 @@ class Pools extends Extension } $this->list_pools($page, $page_num, $search); } elseif ($event->page_matches("pool")) { - $pool_id = 0; - $pool = []; - - // Check if we have pool id, since this is most often the case. - if (isset($_POST["pool_id"])) { - $pool_id = int_escape($_POST["pool_id"]); - $pool = $this->get_single_pool($pool_id); - } - // What action are we trying to perform? switch ($event->get_arg(0)) { case "new": // Show form for new pools @@ -305,6 +296,9 @@ class Pools extends Extension break; case "edit": // Edit the pool (remove images) + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($this->have_permission($user, $pool)) { $result = $database->execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", ["pid" => $pool_id]); $images = []; @@ -319,6 +313,9 @@ class Pools extends Extension break; case "order": // Order the pool (view and change the order of images within the pool) + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if (isset($_POST["order_view"])) { if ($this->have_permission($user, $pool)) { $result = $database->execute( @@ -363,6 +360,9 @@ class Pools extends Extension } break; case "reverse": + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($this->have_permission($user, $pool)) { $database->with_savepoint(function () use ($pool_id) { global $database; @@ -389,6 +389,9 @@ class Pools extends Extension } break; case "import": + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($this->have_permission($user, $pool)) { $images = Search::find_images( limit: $config->get_int(PoolsConfig::MAX_IMPORT_RESULTS, 1000), @@ -401,6 +404,9 @@ class Pools extends Extension break; case "add_posts": + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($this->have_permission($user, $pool)) { $image_ids = array_map('intval', $_POST['check']); send_event(new PoolAddPostsEvent($pool_id, $image_ids)); @@ -412,6 +418,9 @@ class Pools extends Extension break; case "remove_posts": + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($this->have_permission($user, $pool)) { $images = ""; foreach ($_POST['check'] as $imageID) { @@ -435,6 +444,9 @@ class Pools extends Extension break; case "edit_description": + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($this->have_permission($user, $pool)) { $database->execute( "UPDATE pools SET description=:dsc,lastupdated=CURRENT_TIMESTAMP WHERE id=:pid", @@ -451,6 +463,9 @@ class Pools extends Extension case "nuke": // Completely remove the given pool. // -> Only admins and owners may do this + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + if ($user->can(Permissions::POOLS_ADMIN) || $user->id == $pool->user_id) { send_event(new PoolDeletionEvent($pool_id)); $page->set_mode(PageMode::REDIRECT); @@ -575,7 +590,7 @@ class Pools extends Extension } if ($pool && $this->have_permission($user, $pool)) { - $image_order = ($matches[2] ?: 0); + $image_order = (int)($matches[2] ?: 0); $this->add_post($pool->id, $event->image_id, true, $image_order); } } diff --git a/ext/pools/theme.php b/ext/pools/theme.php index 448f067b..30c13144 100644 --- a/ext/pools/theme.php +++ b/ext/pools/theme.php @@ -19,7 +19,7 @@ class PoolsTheme extends Themelet * Adds a block to the panel with information on the pool(s) the image is in. * $navIDs = Multidimensional array containing pool id, info & nav IDs. * - * @param array $navIDs + * @param array $navIDs */ public function pool_info(array $navIDs): void { @@ -30,11 +30,13 @@ class PoolsTheme extends Themelet foreach ($navIDs as $poolID => $poolInfo) { $div = DIV(SHM_A("pool/view/" . $poolID, $poolInfo["info"]->title)); - if (!empty($poolInfo["nav"]["prev"])) { - $div->appendChild(SHM_A("post/view/" . $poolInfo["nav"]["prev"], "Prev", class: "pools_prev_img")); - } - if (!empty($poolInfo["nav"]["next"])) { - $div->appendChild(SHM_A("post/view/" . $poolInfo["nav"]["next"], "Next", class: "pools_next_img")); + if(!empty($poolInfo["nav"])) { + if (!empty($poolInfo["nav"]["prev"])) { + $div->appendChild(SHM_A("post/view/" . $poolInfo["nav"]["prev"], "Prev", class: "pools_prev_img")); + } + if (!empty($poolInfo["nav"]["next"])) { + $div->appendChild(SHM_A("post/view/" . $poolInfo["nav"]["next"], "Next", class: "pools_next_img")); + } } $linksPools->appendChild($div); diff --git a/ext/rating/main.php b/ext/rating/main.php index 3ef0c9a1..62ccb512 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -304,6 +304,7 @@ class Ratings extends Extension $results = $database->get_col("SELECT DISTINCT rating FROM images ORDER BY rating"); $original_values = []; foreach ($results as $result) { + assert(is_string($result)); if (array_key_exists($result, ImageRating::$known_ratings)) { $original_values[$result] = ImageRating::$known_ratings[$result]->name; } else { diff --git a/ext/rating/theme.php b/ext/rating/theme.php index a2b2c5ca..72f6efb1 100644 --- a/ext/rating/theme.php +++ b/ext/rating/theme.php @@ -12,7 +12,7 @@ use function MicroHTML\{A,P,TABLE,TD,TH,TR}; class RatingsTheme extends Themelet { /** - * @param array $ratings + * @param array $ratings * @param string[] $selected_options */ public function get_selection_rater_html(string $name = "rating", array $ratings = [], array $selected_options = []): HTMLElement @@ -30,7 +30,7 @@ class RatingsTheme extends Themelet } /** - * @param ImageRating[] $current_ratings + * @param array $current_ratings */ public function display_form(array $current_ratings): void { diff --git a/ext/relationships/test.php b/ext/relationships/test.php index e9c1bae0..cfaa82ae 100644 --- a/ext/relationships/test.php +++ b/ext/relationships/test.php @@ -23,9 +23,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "pbx"); $image_id_3 = $this->post_image("tests/favicon.png", "pbx"); - $image_1 = Image::by_id($image_id_1); - $image_2 = Image::by_id($image_id_2); - $image_3 = Image::by_id($image_id_3); + $image_1 = null_throws(Image::by_id($image_id_1)); + $image_2 = null_throws(Image::by_id($image_id_2)); + $image_3 = null_throws(Image::by_id($image_id_3)); $this->assertNull($image_1['parent_id']); $this->assertNull($image_2['parent_id']); @@ -48,9 +48,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase send_event(new ImageRelationshipSetEvent($image_2->id, $image_1->id)); // refresh data from database - $image_1 = Image::by_id($image_1->id); - $image_2 = Image::by_id($image_2->id); - $image_3 = Image::by_id($image_3->id); + $image_1 = null_throws(Image::by_id($image_1->id)); + $image_2 = null_throws(Image::by_id($image_2->id)); + $image_3 = null_throws(Image::by_id($image_3->id)); $this->assertNull($image_1['parent_id']); $this->assertEquals($image_1->id, $image_2['parent_id']); @@ -72,9 +72,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase send_event(new ImageRelationshipSetEvent($image_2->id, $image_3->id)); // refresh data from database - $image_1 = Image::by_id($image_1->id); - $image_2 = Image::by_id($image_2->id); - $image_3 = Image::by_id($image_3->id); + $image_1 = null_throws(Image::by_id($image_1->id)); + $image_2 = null_throws(Image::by_id($image_2->id)); + $image_3 = null_throws(Image::by_id($image_3->id)); $this->assertNull($image_1['parent_id']); $this->assertEquals($image_3->id, $image_2['parent_id']); @@ -112,9 +112,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase // FIXME: send_event(new ImageRelationshipSetEvent($image_2->id, null)); // refresh data from database - $image_1 = Image::by_id($image_1->id); - $image_2 = Image::by_id($image_2->id); - $image_3 = Image::by_id($image_3->id); + $image_1 = null_throws(Image::by_id($image_1->id)); + $image_2 = null_throws(Image::by_id($image_2->id)); + $image_3 = null_throws(Image::by_id($image_3->id)); $this->assertNull($image_1['parent_id']); $this->assertNull($image_2['parent_id']); @@ -138,9 +138,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "pbx"); $image_id_3 = $this->post_image("tests/favicon.png", "pbx"); - $image_1 = Image::by_id($image_id_1); - $image_2 = Image::by_id($image_id_2); - $image_3 = Image::by_id($image_id_3); + $image_1 = null_throws(Image::by_id($image_id_1)); + $image_2 = null_throws(Image::by_id($image_id_2)); + $image_3 = null_throws(Image::by_id($image_id_3)); $this->assertNull($image_1['parent_id']); $this->assertNull($image_2['parent_id']); @@ -163,9 +163,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase send_event(new TagSetEvent($image_2, ["pbx", "parent:{$image_1->id}"])); // refresh data from database - $image_1 = Image::by_id($image_1->id); - $image_2 = Image::by_id($image_2->id); - $image_3 = Image::by_id($image_3->id); + $image_1 = null_throws(Image::by_id($image_1->id)); + $image_2 = null_throws(Image::by_id($image_2->id)); + $image_3 = null_throws(Image::by_id($image_3->id)); $this->assertEquals(["pbx"], $image_2->get_tag_array()); $this->assertNull($image_1['parent_id']); @@ -189,9 +189,9 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase send_event(new TagSetEvent($image_3, ["pbx", "child:{$image_1->id}"])); // refresh data from database - $image_1 = Image::by_id($image_1->id); - $image_2 = Image::by_id($image_2->id); - $image_3 = Image::by_id($image_3->id); + $image_1 = null_throws(Image::by_id($image_1->id)); + $image_2 = null_throws(Image::by_id($image_2->id)); + $image_3 = null_throws(Image::by_id($image_3->id)); $this->assertEquals(["pbx"], $image_3->get_tag_array()); $this->assertEquals($image_3->id, $image_1['parent_id']); diff --git a/ext/replace_file/main.php b/ext/replace_file/main.php index 6788dfa2..0ee32fff 100644 --- a/ext/replace_file/main.php +++ b/ext/replace_file/main.php @@ -29,7 +29,7 @@ class ReplaceFile extends Extension $this->theme->display_replace_page($page, $image_id); } elseif($event->method == "POST") { if (!empty($_POST["url"])) { - $tmp_filename = tempnam(ini_get('upload_tmp_dir'), "shimmie_transload"); + $tmp_filename = shm_tempnam("transload"); fetch_url($_POST["url"], $tmp_filename); send_event(new ImageReplaceEvent($image, $tmp_filename)); } elseif (count($_FILES) > 0) { @@ -78,7 +78,7 @@ class ReplaceFile extends Extension // update metadata and save metadata to DB $event->image->hash = $event->new_hash; - $event->image->filesize = filesize($target); + $event->image->filesize = filesize_ex($target); $event->image->set_mime(MimeType::get_for_file($target)); send_event(new MediaCheckPropertiesEvent($image)); $image->save_to_db(); diff --git a/ext/replace_file/test.php b/ext/replace_file/test.php index adadbabf..aecd08be 100644 --- a/ext/replace_file/test.php +++ b/ext/replace_file/test.php @@ -23,7 +23,7 @@ class ReplaceFileTest extends ShimmiePHPUnitTestCase // check that the image is original $image = Image::by_id($image_id); - $old_hash = md5_file("tests/pbx_screenshot.jpg"); + $old_hash = md5_file_ex("tests/pbx_screenshot.jpg"); //$this->assertEquals("pbx_screenshot.jpg", $image->filename); $this->assertEquals("image/jpeg", $image->get_mime()); $this->assertEquals(19774, $image->filesize); @@ -32,9 +32,9 @@ class ReplaceFileTest extends ShimmiePHPUnitTestCase // replace it // create a copy because the file is deleted after upload - $tmpfile = tempnam(sys_get_temp_dir(), "shimmie_test"); + $tmpfile = shm_tempnam("test"); copy("tests/favicon.png", $tmpfile); - $new_hash = md5_file($tmpfile); + $new_hash = md5_file_ex($tmpfile); $_FILES = [ 'data' => [ 'name' => 'favicon.png', diff --git a/ext/report_image/main.php b/ext/report_image/main.php index e4fc501a..7839c5c0 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -40,6 +40,9 @@ class ImageReport } } +/** + * @phpstan-type Report array{id: int, image: Image, reason: string, reporter_name: string} + */ class ReportImage extends Extension { /** @var ReportImageTheme */ @@ -211,7 +214,7 @@ class ReportImage extends Extension } /** - * @return array> + * @return array */ public function get_reported_images(): array { diff --git a/ext/report_image/theme.php b/ext/report_image/theme.php index f82da1dd..3560a7e6 100644 --- a/ext/report_image/theme.php +++ b/ext/report_image/theme.php @@ -6,10 +6,13 @@ namespace Shimmie2; use function MicroHTML\INPUT; +/** + * @phpstan-type Report array{id: int, image: Image, reason: string, reporter_name: string} + */ class ReportImageTheme extends Themelet { /** - * @param array $reports + * @param array $reports */ public function display_reported_images(Page $page, array $reports): void { diff --git a/ext/resize/main.php b/ext/resize/main.php index e10befee..83f4beac 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -94,7 +94,7 @@ class ResizeImage extends Extension if (($fh = @fopen($image_filename, 'rb'))) { //check if gif is animated (via https://www.php.net/manual/en/function.imagecreatefromgif.php#104473) while (!feof($fh) && $isanigif < 2) { - $chunk = fread($fh, 1024 * 100); + $chunk = false_throws(fread($fh, 1024 * 100)); $isanigif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', $chunk, $matches); } } @@ -186,7 +186,7 @@ class ResizeImage extends Extension [$new_width, $new_height] = get_scaled_by_aspect_ratio($event->image->width, $event->image->height, $max_width, $max_height); if ($new_width !== $event->image->width || $new_height !== $event->image->height) { - $tmp_filename = tempnam(sys_get_temp_dir(), 'shimmie_resize'); + $tmp_filename = shm_tempnam('resize'); if (empty($tmp_filename)) { throw new ImageResizeException("Unable to save temporary image file."); } @@ -240,7 +240,7 @@ class ResizeImage extends Extension $hash = $image_obj->hash; $image_filename = warehouse_path(Image::IMAGE_DIR, $hash); - $info = getimagesize($image_filename); + $info = false_throws(getimagesize($image_filename)); if (($image_obj->width != $info[0]) || ($image_obj->height != $info[1])) { throw new ImageResizeException("The current image size does not match what is set in the database! - Aborting Resize."); } @@ -248,7 +248,7 @@ class ResizeImage extends Extension list($new_height, $new_width) = $this->calc_new_size($image_obj, $width, $height); /* Temp storage while we resize */ - $tmp_filename = tempnam(sys_get_temp_dir(), 'shimmie_resize'); + $tmp_filename = shm_tempnam('resize'); if (empty($tmp_filename)) { throw new ImageResizeException("Unable to save temporary image file."); } diff --git a/ext/rotate/main.php b/ext/rotate/main.php index 6da5a429..723be202 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -110,7 +110,7 @@ class RotateImage extends Extension throw new ImageRotateException("$image_filename does not exist."); } - $info = getimagesize($image_filename); + $info = false_throws(getimagesize($image_filename)); $memory_use = Media::calc_memory_use($info); $memory_limit = get_memory_limit(); @@ -121,7 +121,7 @@ class RotateImage extends Extension /* Attempt to load the image */ - $image = imagecreatefromstring(file_get_contents($image_filename)); + $image = imagecreatefromstring(file_get_contents_ex($image_filename)); if ($image == false) { throw new ImageRotateException("Could not load image: ".$image_filename); } @@ -143,7 +143,7 @@ class RotateImage extends Extension } /* Temp storage while we rotate */ - $tmp_filename = tempnam(ini_get('upload_tmp_dir'), 'shimmie_rotate'); + $tmp_filename = shm_tempnam('rotate'); if (empty($tmp_filename)) { throw new ImageRotateException("Unable to save temporary image file."); } @@ -173,7 +173,7 @@ class RotateImage extends Extension throw new ImageRotateException("Could not save image: ".$tmp_filename); } - $new_hash = md5_file($tmp_filename); + $new_hash = md5_file_ex($tmp_filename); /* Move the new image into the main storage location */ $target = warehouse_path(Image::IMAGE_DIR, $new_hash); if (!@copy($tmp_filename, $target)) { diff --git a/ext/rss_comments/main.php b/ext/rss_comments/main.php index 0204eb89..6b487ff0 100644 --- a/ext/rss_comments/main.php +++ b/ext/rss_comments/main.php @@ -40,7 +40,7 @@ class RSSComments extends Extension $comment_id = $comment['comment_id']; $link = make_http(make_link("post/view/$image_id")); $owner = html_escape($comment['user_name']); - $posted = date(DATE_RSS, strtotime($comment['posted'])); + $posted = date(DATE_RSS, strtotime_ex($comment['posted'])); $comment = html_escape($comment['comment']); $content = html_escape("$owner: $comment"); diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index ad3d9403..a61096cc 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -109,7 +109,7 @@ class RSSImages extends Extension $tags = html_escape($image->get_tag_list()); $thumb_url = $image->get_thumb_link(); $image_url = $image->get_image_link(); - $posted = date(DATE_RSS, false_throws(strtotime($image->posted))); + $posted = date(DATE_RSS, strtotime_ex($image->posted)); $content = html_escape( "
" . "

" . $this->theme->build_thumb_html($image) . "

" . diff --git a/ext/s3/main.php b/ext/s3/main.php index c20c9243..c00f08a2 100644 --- a/ext/s3/main.php +++ b/ext/s3/main.php @@ -247,7 +247,7 @@ class S3 extends Extension $client->putObject([ 'Bucket' => $image_bucket, 'Key' => $key, - 'Body' => file_get_contents($image->get_image_filename()), + 'Body' => file_get_contents_ex($image->get_image_filename()), 'ACL' => 'public-read', 'ContentType' => $image->get_mime(), 'ContentDisposition' => "inline; filename=\"$friendly\"", diff --git a/ext/setup/main.php b/ext/setup/main.php index c1f8fe5c..5c3d76b7 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -348,7 +348,7 @@ class Setup extends Extension public function onSetupBuilding(SetupBuildingEvent $event): void { $themes = []; - foreach (false_throws(glob("themes/*")) as $theme_dirname) { + foreach (glob_ex("themes/*") as $theme_dirname) { $name = str_replace("themes/", "", $theme_dirname); $human = str_replace("_", " ", $name); $human = ucwords($human); @@ -423,7 +423,7 @@ class Setup extends Extension } } log_warning("setup", "Configuration updated"); - foreach (glob("data/cache/*.css") as $css_cache) { + foreach (glob_ex("data/cache/*.css") as $css_cache) { unlink($css_cache); } log_warning("setup", "Cache cleared"); diff --git a/ext/sitemap/main.php b/ext/sitemap/main.php index 766130d7..c8c82dda 100644 --- a/ext/sitemap/main.php +++ b/ext/sitemap/main.php @@ -29,7 +29,7 @@ class XMLSitemap extends Extension file_put_contents($cache_path, $xml); } - $xml = file_get_contents($cache_path); + $xml = file_get_contents_ex($cache_path); $page->set_mode(PageMode::DATA); $page->set_mime(MimeType::XML_APPLICATION); $page->set_data($xml); @@ -67,7 +67,7 @@ class XMLSitemap extends Extension "post/view/$image->id", "weekly", "0.8", - date("Y-m-d", strtotime($image->posted)) + date("Y-m-d", strtotime_ex($image->posted)) ); } @@ -87,7 +87,7 @@ class XMLSitemap extends Extension "post/view/$image->id", "monthly", "0.6", - date("Y-m-d", strtotime($image->posted)) + date("Y-m-d", strtotime_ex($image->posted)) ); } diff --git a/ext/source_history/main.php b/ext/source_history/main.php index 4f44b562..7d137da9 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -189,7 +189,7 @@ class SourceHistory extends Extension } if (isset($_POST['revert_ip']) && !empty($_POST['revert_ip'])) { - $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); + $revert_ip = filter_var_ex($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); if ($revert_ip === false) { // invalid ip given. diff --git a/ext/static_files/main.php b/ext/static_files/main.php index 5a7b052d..e6dd42a5 100644 --- a/ext/static_files/main.php +++ b/ext/static_files/main.php @@ -48,7 +48,7 @@ class StaticFiles extends Extension $page->add_http_header("Cache-control: public, max-age=600"); $page->add_http_header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); $page->set_mode(PageMode::DATA); - $page->set_data(file_get_contents($filename)); + $page->set_data(file_get_contents_ex($filename)); $page->set_mime(MimeType::get_for_file($filename)); } diff --git a/ext/tag_categories/main.php b/ext/tag_categories/main.php index d932bd1d..d6718321 100644 --- a/ext/tag_categories/main.php +++ b/ext/tag_categories/main.php @@ -126,7 +126,7 @@ class TagCategories extends Extension $tc_keyed_dict = []; foreach ($tc_dict as $row) { - $tc_keyed_dict[$row['category']] = $row; + $tc_keyed_dict[(string)$row['category']] = $row; } return $tc_keyed_dict; diff --git a/ext/tag_editcloud/info.php b/ext/tag_editcloud/info.php index eeb866ab..a26edb96 100644 --- a/ext/tag_editcloud/info.php +++ b/ext/tag_editcloud/info.php @@ -10,6 +10,6 @@ class TagEditCloudInfo extends ExtensionInfo public string $key = self::KEY; public string $name = "Tag EditCloud"; - public array $authors = ["AtomicDryad", "Luana Latte" => "luana.latte.cat@gmail.com"]; + public array $authors = ["AtomicDryad" => null, "Luana Latte" => "luana.latte.cat@gmail.com"]; public string $description = "Add or remove tags to the editor via clicking."; } diff --git a/ext/tag_editcloud/main.php b/ext/tag_editcloud/main.php index ba9a3ebd..edeb3773 100644 --- a/ext/tag_editcloud/main.php +++ b/ext/tag_editcloud/main.php @@ -163,7 +163,7 @@ class TagEditCloud extends Extension } $size = sprintf("%.2f", max($row['scaled'], 0.5)); - $js = html_escape('tageditcloud_toggle_tag(this,'.json_encode($full_tag).')'); //Ugly, but it works + $js = html_escape('tageditcloud_toggle_tag(this,'.json_encode_ex($full_tag).')'); //Ugly, but it works if (in_array($row['tag'], $image->get_tag_array())) { if ($used_first) { diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index 3758491a..de9c01cb 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -238,7 +238,7 @@ class TagHistory extends Extension } if (isset($_POST['revert_ip']) && !empty($_POST['revert_ip'])) { - $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); + $revert_ip = filter_var_ex($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); if ($revert_ip === false) { // invalid ip given. diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 589416aa..284f7586 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -251,7 +251,7 @@ class TagList extends Extension md5("tc" . $tags_min . $starts_with . VERSION) ); if (file_exists($cache_key)) { - return file_get_contents($cache_key); + return file_get_contents_ex($cache_key); } $tag_data = $database->get_all(" @@ -307,7 +307,7 @@ class TagList extends Extension md5("ta" . $tags_min . $starts_with . VERSION) ); if (file_exists($cache_key)) { - return file_get_contents($cache_key); + return file_get_contents_ex($cache_key); } $tag_data = $database->get_pairs(" @@ -389,7 +389,7 @@ class TagList extends Extension md5("tp" . $tags_min . VERSION) ); if (file_exists($cache_key)) { - return file_get_contents($cache_key); + return file_get_contents_ex($cache_key); } $tag_data = $database->get_all(" diff --git a/ext/tips/main.php b/ext/tips/main.php index 6ab89b97..7d4234d3 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -121,7 +121,7 @@ class Tips extends Extension $data_href = get_base_href(); $url = $data_href."/ext/tips/images/"; - $dirPath = dir('./ext/tips/images'); + $dirPath = dir_ex('./ext/tips/images'); $images = []; while (($file = $dirPath->read()) !== false) { if ($file[0] != ".") { diff --git a/ext/transcode/main.php b/ext/transcode/main.php index 37446fc2..eacb443c 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -391,9 +391,9 @@ class TranscodeImage extends Extension $q = $config->get_int(TranscodeConfig::QUALITY); - $tmp_name = tempnam(sys_get_temp_dir(), "shimmie_transcode"); + $tmp_name = shm_tempnam("transcode"); - $image = imagecreatefromstring(file_get_contents($source_name)); + $image = false_throws(imagecreatefromstring(file_get_contents_ex($source_name))); try { $result = false; switch ($target_mime) { @@ -467,7 +467,7 @@ class TranscodeImage extends Extension break; } - $tmp_name = tempnam(sys_get_temp_dir(), "shimmie_transcode"); + $tmp_name = shm_tempnam("transcode"); $source_type = FileExtension::get_for_mime($source_mime); diff --git a/ext/transcode_video/main.php b/ext/transcode_video/main.php index 68a0b711..5d3bcf42 100644 --- a/ext/transcode_video/main.php +++ b/ext/transcode_video/main.php @@ -211,7 +211,7 @@ class TranscodeVideo extends Extension } $original_file = warehouse_path(Image::IMAGE_DIR, $image->hash); - $tmp_filename = false_throws(tempnam(sys_get_temp_dir(), "shimmie_transcode_video")); + $tmp_filename = shm_tempnam("transcode_video"); $tmp_filename = $this->transcode_video($original_file, $image->video_codec, $target_mime, $tmp_filename); send_event(new ImageReplaceEvent($image, $tmp_filename)); return true; diff --git a/ext/update/main.php b/ext/update/main.php index 307bc104..5b7b5b49 100644 --- a/ext/update/main.php +++ b/ext/update/main.php @@ -94,7 +94,7 @@ class Update extends Extension $zip = new \ZipArchive(); if ($zip->open("./data/update_$commitSHA.zip") === true) { for ($i = 1; $i < $zip->numFiles; $i++) { - $filename = $zip->getNameIndex($i); + $filename = false_throws($zip->getNameIndex($i)); if (substr($filename, -1) !== "/") { copy("zip://".dirname(dirname(__DIR__)).'/'."./data/update_$commitSHA.zip"."#".$filename, substr($filename, 50)); diff --git a/ext/upload/main.php b/ext/upload/main.php index 2ac35958..51246015 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -45,8 +45,8 @@ class DataUploadEvent extends Event { assert(is_readable($tmpname)); $this->tmpname = $tmpname; - $this->hash = md5_file($tmpname); - $this->size = filesize($tmpname); + $this->hash = md5_file_ex($tmpname); + $this->size = filesize_ex($tmpname); $mime = $mime ?? MimeType::get_for_file($tmpname, get_file_ext($this->metadata["filename"]) ?? null); if (empty($mime)) { throw new UploadException("Could not determine mime type"); @@ -381,7 +381,7 @@ class Upload extends Extension global $page, $config, $user, $database; $results = []; - $tmp_filename = tempnam(ini_get('upload_tmp_dir'), "shimmie_transload"); + $tmp_filename = shm_tempnam("transload"); try { // Fetch file diff --git a/ext/upload/test.php b/ext/upload/test.php index 68ed599d..c2093263 100644 --- a/ext/upload/test.php +++ b/ext/upload/test.php @@ -80,7 +80,7 @@ class UploadTest extends ShimmiePHPUnitTestCase public function testRejectHuge(): void { // FIXME: huge.dat is rejected for other reasons; manual testing shows that this works - file_put_contents("data/huge.jpg", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024 * 1024 * 3)); + file_put_contents("data/huge.jpg", file_get_contents_ex("tests/pbx_screenshot.jpg") . str_repeat("U", 1024 * 1024 * 3)); $e = $this->assertException(UploadException::class, function () { $this->post_image("data/huge.jpg", "test"); }); diff --git a/ext/upload/theme.php b/ext/upload/theme.php index ccd20d0c..012ec9b2 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -40,7 +40,7 @@ class UploadTheme extends Themelet $tl_enabled = ($config->get_string(UploadConfig::TRANSLOAD_ENGINE, "none") != "none"); $max_size = $config->get_int(UploadConfig::SIZE); $max_kb = to_shorthand_int($max_size); - $max_total_size = parse_shorthand_int(ini_get('post_max_size')); + $max_total_size = parse_shorthand_int(ini_get('post_max_size') ?: "0"); $max_total_kb = to_shorthand_int($max_total_size); $upload_list = $this->build_upload_list(); @@ -253,7 +253,7 @@ class UploadTheme extends Themelet $max_size = $config->get_int(UploadConfig::SIZE); $max_kb = to_shorthand_int($max_size); - $max_total_size = parse_shorthand_int(ini_get('post_max_size')); + $max_total_size = parse_shorthand_int(ini_get('post_max_size') ?: "0"); $max_total_kb = to_shorthand_int($max_total_size); // diff --git a/ext/user/main.php b/ext/user/main.php index 35080e4b..36384270 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -831,7 +831,7 @@ class UserPage extends Extension "You need to specify the account number to edit" )); } else { - $uid = int_escape($_POST['id']); + $uid = int_escape((string)$_POST['id']); $duser = User::by_id($uid); log_warning("user", "Deleting user #{$uid} (@{$duser->name})"); diff --git a/tests/phpstan.neon b/tests/phpstan.neon index 6bbec296..644b3d7c 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -1,6 +1,6 @@ parameters: errorFormat: raw - level: 6 + level: 7 paths: - ../core - ../ext diff --git a/themes/danbooru/comment.theme.php b/themes/danbooru/comment.theme.php index 1465bf38..fdf36413 100644 --- a/themes/danbooru/comment.theme.php +++ b/themes/danbooru/comment.theme.php @@ -112,7 +112,7 @@ class CustomCommentListTheme extends CommentListTheme $h_del = ""; if ($user->can(Permissions::DELETE_COMMENT)) { $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $j_delete_confirm_message = json_encode_ex("Delete comment by {$comment->owner_name}:\n$comment_preview"); $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); $h_del = " - Del"; diff --git a/themes/danbooru2/comment.theme.php b/themes/danbooru2/comment.theme.php index a6525e0c..df45e10a 100644 --- a/themes/danbooru2/comment.theme.php +++ b/themes/danbooru2/comment.theme.php @@ -112,7 +112,7 @@ class CustomCommentListTheme extends CommentListTheme $h_del = ""; if ($user->can(Permissions::DELETE_COMMENT)) { $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $j_delete_confirm_message = json_encode_ex("Delete comment by {$comment->owner_name}:\n$comment_preview"); $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); $h_del = " - Del"; diff --git a/themes/futaba/comment.theme.php b/themes/futaba/comment.theme.php index 3a015f05..0f247df2 100644 --- a/themes/futaba/comment.theme.php +++ b/themes/futaba/comment.theme.php @@ -86,7 +86,7 @@ class CustomCommentListTheme extends CommentListTheme $h_del = ""; if ($user->can(Permissions::DELETE_COMMENT)) { $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $j_delete_confirm_message = json_encode_ex("Delete comment by {$comment->owner_name}:\n$comment_preview"); $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); $h_del = " - Del"; diff --git a/themes/rule34v2/page.class.php b/themes/rule34v2/page.class.php index b96b423b..e11cd76c 100644 --- a/themes/rule34v2/page.class.php +++ b/themes/rule34v2/page.class.php @@ -87,7 +87,7 @@ EOD; $generated = autodate(date('c')); $footer_html = $this->footer_html(); - $header_inc = file_get_contents("themes/rule34v2/header.inc"); + $header_inc = file_get_contents_ex("themes/rule34v2/header.inc"); assert($header_inc !== false); $header_inc = str_replace('$QUERY', $query, $header_inc); return <<