diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/colors.php | 204 | ||||
-rwxr-xr-x | include/controls.php | 70 | ||||
-rw-r--r-- | include/controls_compat.php | 23 | ||||
-rw-r--r-- | include/errorhandler.php | 13 | ||||
-rw-r--r-- | include/functions.php | 178 | ||||
-rw-r--r-- | include/sessions.php | 36 |
6 files changed, 358 insertions, 166 deletions
diff --git a/include/colors.php b/include/colors.php index 41158f96e..5682b55a1 100644 --- a/include/colors.php +++ b/include/colors.php @@ -5,7 +5,7 @@ if (file_exists("lib/floIcon.php")) { require_once "lib/floIcon.php"; } -function _resolve_htmlcolor($color) { +function _resolve_htmlcolor(string $color): string { $htmlcolors = array ("aliceblue" => "#f0f8ff", "antiquewhite" => "#faebd7", "aqua" => "#00ffff", @@ -162,8 +162,14 @@ function _resolve_htmlcolor($color) { return $color; } -### RGB >> HSL -function _color_rgb2hsl($rgb) { +/** + * RGB >> HSL + * + * @param array{0: int, 1: int, 2: int} $rgb + * + * @return array{0: float, 1: float, 2: float} + */ +function _color_rgb2hsl(array $rgb): array { $r = $rgb[0]; $g = $rgb[1]; $b = $rgb[2]; $min = min($r, min($g, $b)); $max = max($r, max($g, $b)); $delta = $max - $min; $l = ($min + $max) / 2; $s = 0; @@ -179,8 +185,14 @@ function _color_rgb2hsl($rgb) { } return array($h, $s, $l); } -### HSL >> RGB -function _color_hsl2rgb($hsl) { +/** + * HSL >> RGB + * + * @param array{0: float, 1: float, 2: float} $hsl + * + * @return array{0: int, 1: int, 2: int} + */ +function _color_hsl2rgb($hsl): array { $h = $hsl[0]; $s = $hsl[1]; $l = $hsl[2]; $m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s; $m1 = $l * 2 - $m2; @@ -190,40 +202,65 @@ function _color_hsl2rgb($hsl) { } ### Helper function for _color_hsl2rgb(). -function _color_hue2rgb($m1, $m2, $h) { - $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h); - if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6; - if ($h * 2 < 1) return $m2; - if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6; - return $m1; +function _color_hue2rgb(float $m1, float $m2, float $h): int { + $rv = $m1; + + $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h); + + if ($h * 6 < 1) { + $rv = $m1 + ($m2 - $m1) * $h * 6; + } else if ($h * 2 < 1) { + $rv = $m2; + } else if ($h * 3 < 2) { + $rv = $m1 + ($m2 - $m1) * (0.66666 - $h) * 6; + } + + return (int) round($rv); } -### Convert a hex color into an RGB triplet. -function _color_unpack($hex, $normalize = false) { +/** + * Convert a hex color into an RGB triplet. + * + * @return array{0: int, 1: int, 2: int} + */ +function _color_unpack(string $hex, bool $normalize = false): array { + $hex = strpos($hex, '#') !== 0 ? _resolve_htmlcolor($hex) : substr($hex, 1); - if (strpos($hex, '#') !== 0) - $hex = _resolve_htmlcolor($hex); - else - $hex = substr($hex, 1); + if (strlen($hex) == 4) { + $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3]; + } - if (strlen($hex) == 4) { - $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3]; - } - $c = hexdec($hex); - for ($i = 16; $i >= 0; $i -= 8) { - $out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1); - } return $out; + $c = hexdec($hex); + $out = []; + + for ($i = 16; $i >= 0; $i -= 8) { + $out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1); + } + + return $out; } -### Convert an RGB triplet to a hex color. -function _color_pack($rgb, $normalize = false) { +/** + * Convert an RGB triplet to a hex color. + * + * @param array{0: int, 1: int, 2: int} $rgb + */ +function _color_pack(array $rgb, bool $normalize = false): string { $out = 0; - foreach ($rgb as $k => $v) { - $out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8)); - }return '#'. str_pad(dechex($out), 6, 0, STR_PAD_LEFT); + + foreach ($rgb as $k => $v) { + $out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8)); + } + + return '#'. str_pad(dechex($out), 6, '0', STR_PAD_LEFT); } -function rgb2hsl($arr) { +/** + * @param array{0: int, 1: int, 2: int} $arr + * + * @return array{0: float, 1: float, 2: float} + */ +function rgb2hsl(array $arr): array { $r = $arr[0]; $g = $arr[1]; $b = $arr[2]; @@ -248,9 +285,14 @@ function rgb2hsl($arr) { $del_G = ((($var_Max - $var_G ) / 6 ) + ($del_Max / 2 ) ) / $del_Max; $del_B = ((($var_Max - $var_B ) / 6 ) + ($del_Max / 2 ) ) / $del_Max; - if ($var_R == $var_Max) $h = $del_B - $del_G; - else if ($var_G == $var_Max) $h = (1 / 3 ) + $del_R - $del_B; - else if ($var_B == $var_Max) $h = (2 / 3 ) + $del_G - $del_R; + if ($var_R == $var_Max) { + $h = $del_B - $del_G; + } else if ($var_G == $var_Max) { + $h = (1 / 3 ) + $del_R - $del_B; + } else { + // ($var_B == $var_Max) + $h = (2 / 3 ) + $del_G - $del_R; + } if ($h < 0) $h++; if ($h > 1) $h--; @@ -259,7 +301,12 @@ function rgb2hsl($arr) { return array($h, $s, $v); } -function hsl2rgb($arr) { +/** + * @param array{0: float, 1: float, 2: float} $arr + * + * @return array{0: int, 1: int, 2: int} + */ +function hsl2rgb(array $arr): array { $h = $arr[0]; $s = $arr[1]; $v = $arr[2]; @@ -280,63 +327,66 @@ function hsl2rgb($arr) { else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $v ; } else { $var_R = $v ; $var_G = $var_1 ; $var_B = $var_2 ; } - $r = $var_R * 255; - $g = $var_G * 255; - $B = $var_B * 255; + $r = (int) round($var_R * 255); + $g = (int) round($var_G * 255); + $B = (int) round($var_B * 255); } return array($r, $g, $B); } - function colorPalette($imageFile, $numColors, $granularity = 5) { - $granularity = max(1, abs((int)$granularity)); - $colors = array(); - - $size = @getimagesize($imageFile); - - // to enable .ico support place floIcon.php into lib/ - if (strtolower($size['mime']) == 'image/vnd.microsoft.icon') { - - if (class_exists("floIcon")) { +/** + * @return array<int, string>|null +*/ +function colorPalette(string $imageFile, int $numColors, int $granularity = 5): ?array { + $granularity = max(1, abs($granularity)); + $colors = []; - $ico = new \floIcon(); - @$ico->readICO($imageFile); + $size = @getimagesize($imageFile); + $img = null; - if(count($ico->images)==0) - return false; - else - $img = @$ico->images[count($ico->images)-1]->getImageResource(); + // to enable .ico support place floIcon.php into lib/ + if (strtolower($size['mime']) == 'image/vnd.microsoft.icon') { + if (class_exists("floIcon")) { + $ico = new \floIcon(); + @$ico->readICO($imageFile); + if(count($ico->images) == 0) { + return null; } else { - return false; + $img = @$ico->images[count($ico->images)-1]->getImageResource(); } - - } else if ($size[0] > 0 && $size[1] > 0) { - $img = @imagecreatefromstring(file_get_contents($imageFile)); } + return null; + } else if ($size[0] > 0 && $size[1] > 0) { + $img = @imagecreatefromstring(file_get_contents($imageFile)); + } - if (!$img) return false; - - for($x = 0; $x < $size[0]; $x += $granularity) { - for($y = 0; $y < $size[1]; $y += $granularity) { - $thisColor = imagecolorat($img, $x, $y); - $rgb = imagecolorsforindex($img, $thisColor); - $red = round(round(($rgb['red'] / 0x33)) * 0x33); - $green = round(round(($rgb['green'] / 0x33)) * 0x33); - $blue = round(round(($rgb['blue'] / 0x33)) * 0x33); - $thisRGB = sprintf('%02X%02X%02X', $red, $green, $blue); - if(array_key_exists($thisRGB, $colors)) { - $colors[$thisRGB]++; - } else{ - $colors[$thisRGB] = 1; - } - } - } + if (!$img) { + return null; + } - arsort($colors); - return array_slice(array_keys($colors), 0, $numColors); + for($x = 0; $x < $size[0]; $x += $granularity) { + for($y = 0; $y < $size[1]; $y += $granularity) { + $thisColor = imagecolorat($img, $x, $y); + $rgb = imagecolorsforindex($img, $thisColor); + $red = round(round(($rgb['red'] / 0x33)) * 0x33); + $green = round(round(($rgb['green'] / 0x33)) * 0x33); + $blue = round(round(($rgb['blue'] / 0x33)) * 0x33); + $thisRGB = sprintf('%02X%02X%02X', $red, $green, $blue); + + if(array_key_exists($thisRGB, $colors)) { + $colors[$thisRGB]++; + } else { + $colors[$thisRGB] = 1; + } + } } - function calculate_avg_color($iconFile) { + arsort($colors); + return array_slice(array_keys($colors), 0, $numColors); +} + + function calculate_avg_color(string $iconFile): string { $palette = colorPalette($iconFile, 4, 4); if (is_array($palette)) { diff --git a/include/controls.php b/include/controls.php index a1a1bc59b..1ea019769 100755 --- a/include/controls.php +++ b/include/controls.php @@ -1,7 +1,10 @@ <?php namespace Controls; - function attributes_to_string(array $attributes) { + /** + * @param array<string, mixed> $attributes + */ + function attributes_to_string(array $attributes): string { $rv = []; foreach ($attributes as $k => $v) { @@ -21,21 +24,27 @@ return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method)); } */ - function public_method_tags(\Plugin $plugin, string $method) { + function public_method_tags(\Plugin $plugin, string $method): string { return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method)); } - function pluginhandler_tags(\Plugin $plugin, string $method) { + function pluginhandler_tags(\Plugin $plugin, string $method): string { return hidden_tag("op", "pluginhandler") . hidden_tag("plugin", strtolower(get_class($plugin))) . hidden_tag("method", $method); } - function button_tag(string $value, string $type, array $attributes = []) { + /** + * @param array<string, mixed> $attributes + */ + function button_tag(string $value, string $type, array $attributes = []): string { return "<button dojoType=\"dijit.form.Button\" ".attributes_to_string($attributes)." type=\"$type\">$value</button>"; } - function input_tag(string $name, string $value, string $type = "text", array $attributes = [], string $id = "") { + /** + * @param array<string, mixed> $attributes + */ + function input_tag(string $name, string $value, string $type = "text", array $attributes = [], string $id = ""): string { $attributes_str = attributes_to_string($attributes); $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='dijit.form.TextBox'" : ""; @@ -43,23 +52,40 @@ type=\"$type\" value=\"".htmlspecialchars($value)."\">"; } - function number_spinner_tag(string $name, string $value, array $attributes = [], string $id = "") { + /** + * @param array<string, mixed> $attributes + */ + function number_spinner_tag(string $name, string $value, array $attributes = [], string $id = ""): string { return input_tag($name, $value, "text", array_merge(["dojoType" => "dijit.form.NumberSpinner"], $attributes), $id); } - function submit_tag(string $value, array $attributes = []) { + /** + * @param array<string, mixed> $attributes + */ + function submit_tag(string $value, array $attributes = []): string { return button_tag($value, "submit", array_merge(["class" => "alt-primary"], $attributes)); } - function cancel_dialog_tag(string $value, array $attributes = []) { + /** + * @param array<string, mixed> $attributes + */ + function cancel_dialog_tag(string $value, array $attributes = []): string { return button_tag($value, "", array_merge(["onclick" => "App.dialogOf(this).hide()"], $attributes)); } - function icon(string $icon, array $attributes = []) { + /** + * @param array<string, mixed> $attributes + */ + function icon(string $icon, array $attributes = []): string { return "<i class=\"material-icons\" ".attributes_to_string($attributes).">$icon</i>"; } - function select_tag(string $name, $value, array $values, array $attributes = [], string $id = "") { + /** + * @param mixed $value + * @param array<int|string, string> $values + * @param array<string, mixed> $attributes + */ + function select_tag(string $name, $value, array $values, array $attributes = [], string $id = ""): string { $attributes_str = attributes_to_string($attributes); $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='fox.form.Select'" : ""; @@ -83,7 +109,12 @@ return select_tag($name, $value, $values, $attributes, $id); }*/ - function select_hash(string $name, $value, array $values, array $attributes = [], string $id = "") { + /** + * @param mixed $value + * @param array<int|string, string> $values + * @param array<string, mixed> $attributes + */ + function select_hash(string $name, $value, array $values, array $attributes = [], string $id = ""): string { $attributes_str = attributes_to_string($attributes); $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='fox.form.Select'" : ""; @@ -93,7 +124,7 @@ foreach ($values as $k => $v) { $is_sel = ($k == $value) ? "selected=\"selected\"" : ""; - $rv .= "<option value=\"".htmlspecialchars($k)."\" $is_sel>".htmlspecialchars($v)."</option>"; + $rv .= "<option value=\"".htmlspecialchars("$k")."\" $is_sel>".htmlspecialchars($v)."</option>"; } $rv .= "</select>"; @@ -101,13 +132,19 @@ return $rv; } - function hidden_tag(string $name, string $value, array $attributes = []) { + /** + * @param array<string, mixed> $attributes + */ + function hidden_tag(string $name, string $value, array $attributes = []): string { return "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" ".attributes_to_string($attributes)." name=\"".htmlspecialchars($name)."\" value=\"".htmlspecialchars($value)."\">"; } - function checkbox_tag(string $name, bool $checked = false, string $value = "", array $attributes = [], string $id = "") { + /** + * @param array<string, mixed> $attributes + */ + function checkbox_tag(string $name, bool $checked = false, string $value = "", array $attributes = [], string $id = ""): string { $is_checked = $checked ? "checked" : ""; $value_str = $value ? "value=\"".htmlspecialchars($value)."\"" : ""; @@ -115,8 +152,11 @@ $value_str $is_checked ".attributes_to_string($attributes)." id=\"".htmlspecialchars($id)."\">"; } + /** + * @param array<string, mixed> $attributes + */ function select_feeds_cats(string $name, int $default_id = null, array $attributes = [], - bool $include_all_cats = true, string $root_id = null, int $nest_level = 0, string $id = "") { + bool $include_all_cats = true, string $root_id = null, int $nest_level = 0, string $id = ""): string { $ret = ""; diff --git a/include/controls_compat.php b/include/controls_compat.php index d1c2c12b5..5a2a9f2ba 100644 --- a/include/controls_compat.php +++ b/include/controls_compat.php @@ -1,7 +1,9 @@ <?php - -function stylesheet_tag($filename, $attributes = []) { +/** + * @param array<string, mixed> $attributes + */ +function stylesheet_tag(string $filename, array $attributes = []): string { $attributes_str = \Controls\attributes_to_string( array_merge( @@ -16,7 +18,10 @@ function stylesheet_tag($filename, $attributes = []) { return "<link $attributes_str/>\n"; } -function javascript_tag($filename, $attributes = []) { +/** + * @param array<string, mixed> $attributes + */ +function javascript_tag(string $filename, array $attributes = []): string { $attributes_str = \Controls\attributes_to_string( array_merge( [ @@ -29,26 +34,26 @@ function javascript_tag($filename, $attributes = []) { return "<script $attributes_str></script>\n"; } -function format_warning($msg, $id = "") { +function format_warning(string $msg, string $id = ""): string { return "<div class=\"alert\" id=\"$id\">$msg</div>"; } -function format_notice($msg, $id = "") { +function format_notice(string $msg, string $id = ""): string { return "<div class=\"alert alert-info\" id=\"$id\">$msg</div>"; } -function format_error($msg, $id = "") { +function format_error(string $msg, string $id = ""): string { return "<div class=\"alert alert-danger\" id=\"$id\">$msg</div>"; } -function print_notice($msg) { +function print_notice(string $msg): string { return print format_notice($msg); } -function print_warning($msg) { +function print_warning(string $msg): string { return print format_warning($msg); } -function print_error($msg) { +function print_error(string $msg): string { return print format_error($msg); } diff --git a/include/errorhandler.php b/include/errorhandler.php index 2ad0be062..30b6902b3 100644 --- a/include/errorhandler.php +++ b/include/errorhandler.php @@ -1,5 +1,8 @@ <?php -function format_backtrace($trace) { +/** + * @param array<int, array<string, mixed>> $trace + */ +function format_backtrace($trace): string { $rv = ""; $idx = 1; @@ -8,7 +11,7 @@ function format_backtrace($trace) { if (isset($e["file"]) && isset($e["line"])) { $fmt_args = []; - if (is_array($e["args"])) { + if (is_array($e["args"] ?? false)) { foreach ($e["args"] as $a) { if (is_object($a)) { array_push($fmt_args, "{" . get_class($a) . "}"); @@ -16,7 +19,7 @@ function format_backtrace($trace) { array_push($fmt_args, "[" . truncate_string(json_encode($a), 256, "...")) . "]"; } else if (is_resource($a)) { array_push($fmt_args, truncate_string(get_resource_type($a), 256, "...")); - } else { + } else if (is_string($a)) { array_push($fmt_args, truncate_string($a, 256, "...")); } } @@ -39,7 +42,7 @@ function format_backtrace($trace) { return $rv; } -function ttrss_error_handler($errno, $errstr, $file, $line) { +function ttrss_error_handler(int $errno, string $errstr, string $file, int $line): bool { /*if (version_compare(PHP_VERSION, '8.0.0', '<')) { if (error_reporting() == 0 || !$errno) return false; } else { @@ -59,7 +62,7 @@ function ttrss_error_handler($errno, $errstr, $file, $line) { return false; } -function ttrss_fatal_handler() { +function ttrss_fatal_handler(): bool { $error = error_get_last(); if ($error !== NULL) { diff --git a/include/functions.php b/include/functions.php index 922d3765c..91fb62ec5 100644 --- a/include/functions.php +++ b/include/functions.php @@ -2,7 +2,7 @@ define('LABEL_BASE_INDEX', -1024); define('PLUGIN_FEED_BASE_INDEX', -128); - /** constant is @deprecated, use Config::SCHEMA_VERSION instead */ + /** @deprecated by Config::SCHEMA_VERSION */ define('SCHEMA_VERSION', Config::SCHEMA_VERSION); if (version_compare(PHP_VERSION, '8.0.0', '<')) { @@ -36,15 +36,24 @@ define('SUBSTRING_FOR_DATE', 'SUBSTRING'); } + /** + * @return bool|int|null|string + */ function get_pref(string $pref_name, int $owner_uid = null) { return Prefs::get($pref_name, $owner_uid ? $owner_uid : $_SESSION["uid"], $_SESSION["profile"] ?? null); } - function set_pref(string $pref_name, $value, int $owner_uid = null, bool $strip_tags = true) { + /** + * @param bool|int|string $value + */ + function set_pref(string $pref_name, $value, int $owner_uid = null, bool $strip_tags = true): bool { return Prefs::set($pref_name, $value, $owner_uid ? $owner_uid : $_SESSION["uid"], $_SESSION["profile"] ?? null, $strip_tags); } - function get_translations() { + /** + * @return array<string, string> + */ + function get_translations(): array { $t = array( "auto" => __("Detect automatically"), "ar_SA" => "العربيّة (Arabic)", @@ -81,7 +90,7 @@ require_once "lib/gettext/gettext.inc.php"; - function startup_gettext() { + function startup_gettext(): void { $selected_locale = ""; @@ -157,75 +166,115 @@ require_once 'controls.php'; require_once 'controls_compat.php'; - define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . Config::get_version() . ' (http://tt-rss.org/)'); - ini_set('user_agent', SELF_USER_AGENT); + ini_set('user_agent', Config::get_user_agent()); /* compat shims */ - /** function is @deprecated by Config::get_version() */ + /** + * @deprecated by Config::get_version() + * + * @return array<string, mixed>|string + */ function get_version() { return Config::get_version(); } - /** function is @deprecated by Config::get_schema_version() */ - function get_schema_version() { + /** @deprecated by Config::get_schema_version() */ + function get_schema_version(): int { return Config::get_schema_version(); } - /** function is @deprecated by Debug::log() */ - function _debug($msg) { - Debug::log($msg); + /** @deprecated by Debug::log() */ + function _debug(string $msg): void { + Debug::log($msg); } - /** function is @deprecated */ - function getFeedUnread($feed, $is_cat = false) { + + /** @deprecated by Feeds::_get_counters() + * @param int|string $feed feed id or tag name + * @param bool $is_cat + * @return int + * @throws PDOException + */ + function getFeedUnread($feed, bool $is_cat = false): int { return Feeds::_get_counters($feed, $is_cat, true, $_SESSION["uid"]); } - /** function is @deprecated by Sanitizer::sanitize() */ - function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) { + /** + * @deprecated by Sanitizer::sanitize() + * + * @param array<int, string>|null $highlight_words Words to highlight in the HTML output. + * + * @return false|string The HTML, or false if an error occurred. + */ + function sanitize(string $str, bool $force_remove_images = false, int $owner = null, string $site_url = null, array $highlight_words = null, int $article_id = null) { return Sanitizer::sanitize($str, $force_remove_images, $owner, $site_url, $highlight_words, $article_id); } - /** function is @deprecated by UrlHelper::fetch() */ + /** + * @deprecated by UrlHelper::fetch() + * + * @param array<string, bool|int|string>|string $params + * @return bool|string false if something went wrong, otherwise string contents + */ function fetch_file_contents($params) { return UrlHelper::fetch($params); } - /** function is @deprecated by UrlHelper::rewrite_relative() */ + /** + * @deprecated by UrlHelper::rewrite_relative() + * + * Converts a (possibly) relative URL to a absolute one, using provided base URL. + * Provides some exceptions for additional schemes like data: if called with owning element/attribute. + * + * @param string $base_url Base URL (i.e. from where the document is) + * @param string $rel_url Possibly relative URL in the document + * + * @return string Absolute URL + */ function rewrite_relative_url($base_url, $rel_url) { return UrlHelper::rewrite_relative($base_url, $rel_url); } - /** function is @deprecated by UrlHelper::validate() */ - function validate_url($url) { + /** + * @deprecated by UrlHelper::validate() + * + * @return bool|string false if something went wrong, otherwise the URL string + */ + function validate_url(string $url) { return UrlHelper::validate($url); } - /** function is @deprecated by UserHelper::authenticate() */ - function authenticate_user($login, $password, $check_only = false, $service = false) { + /** @deprecated by UserHelper::authenticate() */ + function authenticate_user(string $login = null, string $password = null, bool $check_only = false, string $service = null): bool { return UserHelper::authenticate($login, $password, $check_only, $service); } - /** function is @deprecated by TimeHelper::smart_date_time() */ - function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false, $eta_min = false) { + /** @deprecated by TimeHelper::smart_date_time() */ + function smart_date_time(int $timestamp, int $tz_offset = 0, int $owner_uid = null, bool $eta_min = false): string { return TimeHelper::smart_date_time($timestamp, $tz_offset, $owner_uid, $eta_min); } - /** function is @deprecated by TimeHelper::make_local_datetime() */ - function make_local_datetime($timestamp, $long, $owner_uid = false, $no_smart_dt = false, $eta_min = false) { + /** @deprecated by TimeHelper::make_local_datetime() */ + function make_local_datetime(string $timestamp, bool $long, int $owner_uid = null, bool $no_smart_dt = false, bool $eta_min = false): string { return TimeHelper::make_local_datetime($timestamp, $long, $owner_uid, $no_smart_dt, $eta_min); } // this returns Config::SELF_URL_PATH sans ending slash - /** function is @deprecated by Config::get_self_url() */ - function get_self_url_prefix() { + /** @deprecated by Config::get_self_url() */ + function get_self_url_prefix(): string { return Config::get_self_url(); } /* end compat shims */ - // this is used for user http parameters unless HTML code is actually needed + /** + * This is used for user http parameters unless HTML code is actually needed. + * + * @param mixed $param + * + * @return mixed|null + */ function clean($param) { if (is_array($param)) { return array_map("trim", array_map("strip_tags", $param)); @@ -244,7 +293,7 @@ } } - function make_password($length = 12) { + function make_password(int $length = 12): string { $password = ""; $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ*%+^"; @@ -269,11 +318,11 @@ return $password; } - function validate_csrf($csrf_token) { + function validate_csrf(?string $csrf_token): bool { return isset($csrf_token) && hash_equals($_SESSION['csrf_token'] ?? "", $csrf_token); } - function truncate_string($str, $max_len, $suffix = '…') { + function truncate_string(string $str, int $max_len, string $suffix = '…'): string { if (mb_strlen($str, "utf-8") > $max_len) { return mb_substr($str, 0, $max_len, "utf-8") . $suffix; } else { @@ -281,7 +330,7 @@ } } - function mb_substr_replace($original, $replacement, $position, $length) { + function mb_substr_replace(string $original, string $replacement, int $position, int $length): string { $startString = mb_substr($original, 0, $position, "UTF-8"); $endString = mb_substr($original, $position + $length, mb_strlen($original), "UTF-8"); @@ -290,7 +339,7 @@ return $out; } - function truncate_middle($str, $max_len, $suffix = '…') { + function truncate_middle(string $str, int $max_len, string $suffix = '…'): string { if (mb_strlen($str) > $max_len) { return mb_substr_replace($str, $suffix, $max_len / 2, mb_strlen($str) - $max_len); } else { @@ -298,15 +347,20 @@ } } - function sql_bool_to_bool($s) { + /** Convert values accepted by tt-rss as true/false to PHP booleans + * @see https://tt-rss.org/wiki/ApiReference#boolean-values + * @param null|string $s null values are considered false + * @return bool + */ + function sql_bool_to_bool(?string $s): bool { return $s && ($s !== "f" && $s !== "false"); //no-op for PDO, backwards compat for legacy layer } - function bool_to_sql_bool($s) { + function bool_to_sql_bool(bool $s): int { return $s ? 1 : 0; } - function file_is_locked($filename) { + function file_is_locked(string $filename): bool { if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/$filename")) { if (function_exists('flock')) { $fp = @fopen(Config::get(Config::LOCK_DIRECTORY) . "/$filename", "r"); @@ -328,7 +382,10 @@ } } - function make_lockfile($filename) { + /** + * @return resource|false A file pointer resource on success, or false on error. + */ + function make_lockfile(string $filename) { $fp = fopen(Config::get(Config::LOCK_DIRECTORY) . "/$filename", "w"); if ($fp && flock($fp, LOCK_EX | LOCK_NB)) { @@ -352,38 +409,44 @@ } } - function checkbox_to_sql_bool($val) { + /** + * @param mixed $val + */ + function checkbox_to_sql_bool($val): int { return ($val == "on") ? 1 : 0; } - function uniqid_short() { + function uniqid_short(): string { return uniqid(base_convert((string)rand(), 10, 36)); } - function T_sprintf() { + function T_sprintf(): string { $args = func_get_args(); return vsprintf(__(array_shift($args)), $args); } - function T_nsprintf() { + function T_nsprintf(): string { $args = func_get_args(); return vsprintf(_ngettext(array_shift($args), array_shift($args), array_shift($args)), $args); } - function init_plugins() { + function init_plugins(): bool { PluginHost::getInstance()->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL); return true; } if (!function_exists('gzdecode')) { - function gzdecode($string) { // no support for 2nd argument + /** + * @return false|string The decoded string or false if an error occurred. + */ + function gzdecode(string $string) { // no support for 2nd argument return file_get_contents('compress.zlib://data:who/cares;base64,'. base64_encode($string)); } } - function get_random_bytes($length) { + function get_random_bytes(int $length): string { if (function_exists('random_bytes')) { return random_bytes($length); } else if (function_exists('openssl_random_pseudo_bytes')) { @@ -398,7 +461,7 @@ } } - function read_stdin() { + function read_stdin(): ?string { $fp = fopen("php://stdin", "r"); if ($fp) { @@ -410,11 +473,19 @@ return null; } - function implements_interface($class, $interface) { - return in_array($interface, class_implements($class)); + /** + * @param object|string $class + */ + function implements_interface($class, string $interface): bool { + $class_implemented_interfaces = class_implements($class); + + if ($class_implemented_interfaces) { + return in_array($interface, $class_implemented_interfaces); + } + return false; } - function get_theme_path($theme) { + function get_theme_path(string $theme): string { $check = "themes/$theme"; if (file_exists($check)) return $check; @@ -424,15 +495,18 @@ return ""; } - function theme_exists($theme) { + function theme_exists(string $theme): bool { return file_exists("themes/$theme") || file_exists("themes.local/$theme"); } - function arr_qmarks($arr) { + /** + * @param array<int, mixed> $arr + */ + function arr_qmarks(array $arr): string { return str_repeat('?,', count($arr) - 1) . '?'; } - function get_scripts_timestamp() { + function get_scripts_timestamp(): int { $files = glob("js/*.js"); $ts = 0; diff --git a/include/sessions.php b/include/sessions.php index 7f61f6dbe..48afd0a8b 100644 --- a/include/sessions.php +++ b/include/sessions.php @@ -1,6 +1,8 @@ <?php namespace Sessions; + use UserHelper; + require_once "autoload.php"; require_once "functions.php"; require_once "errorhandler.php"; @@ -19,7 +21,17 @@ ini_set("session.gc_maxlifetime", $session_expire); ini_set("session.cookie_lifetime", "0"); - function validate_session() { + // prolong PHP session cookie + if (isset($_COOKIE[$session_name])) + setcookie($session_name, + $_COOKIE[$session_name], + time() + $session_expire, + ini_get("session.cookie_path"), + ini_get("session.cookie_domain"), + ini_get("session.cookie_secure"), + ini_get("session.cookie_httponly")); + + function validate_session(): bool { if (\Config::get(\Config::SINGLE_USER_MODE)) return true; $pdo = \Db::pdo(); @@ -32,6 +44,11 @@ $_SESSION["login_error_msg"] = __("Session failed to validate (password changed)"); return false; } + + if ($user->access_level == UserHelper::ACCESS_LEVEL_DISABLED) { + $_SESSION["login_error_msg"] = __("Session failed to validate (account is disabled)"); + return false; + } } else { $_SESSION["login_error_msg"] = __("Session failed to validate (user not found)"); return false; @@ -41,11 +58,11 @@ return true; } - function ttrss_open ($s, $n) { + function ttrss_open(string $savePath, string $sessionName): bool { return true; } - function ttrss_read ($id){ + function ttrss_read(string $id): string { global $session_expire; $sth = \Db::pdo()->prepare("SELECT data FROM ttrss_sessions WHERE id=?"); @@ -67,7 +84,7 @@ } - function ttrss_write ($id, $data) { + function ttrss_write(string $id, string $data): bool { global $session_expire; $data = base64_encode($data); @@ -88,18 +105,18 @@ return true; } - function ttrss_close () { + function ttrss_close(): bool { return true; } - function ttrss_destroy($id) { + function ttrss_destroy(string $id): bool { $sth = \Db::pdo()->prepare("DELETE FROM ttrss_sessions WHERE id = ?"); $sth->execute([$id]); return true; } - function ttrss_gc ($expire) { + function ttrss_gc(int $lifetime): bool { \Db::pdo()->query("DELETE FROM ttrss_sessions WHERE expire < " . time()); return true; @@ -109,7 +126,10 @@ session_set_save_handler('\Sessions\ttrss_open', '\Sessions\ttrss_close', '\Sessions\ttrss_read', '\Sessions\ttrss_write', '\Sessions\ttrss_destroy', - '\Sessions\ttrss_gc'); + '\Sessions\ttrss_gc'); // @phpstan-ignore-line + // PHPStan complains about '\Sessions\ttrss_gc' if its $lifetime param isn't marked as string, + // but the docs say it's an int. If it is actually a string it'll get coerced to an int. + register_shutdown_function('session_write_close'); if (!defined('NO_SESSION_AUTOSTART')) { |