aboutsummaryrefslogtreecommitdiff
path: root/lib/lib_rss.php
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2023-01-29 18:53:51 +0100
committerGravatar GitHub <noreply@github.com> 2023-01-29 18:53:51 +0100
commit4f316b2ed397bb331ef89f2cd2d8ce92a725ccba (patch)
tree6d74cfa825724d483d43b23fdf90aadb1e46262a /lib/lib_rss.php
parent2303b29e68d16fbf0a173ab2b4b0ac736041905c (diff)
PHPStan level 9 for ./p/ and lib_rss.php (#5049)
And app/FreshRSS.php Contributes to https://github.com/FreshRSS/FreshRSS/issues/4112
Diffstat (limited to 'lib/lib_rss.php')
-rw-r--r--lib/lib_rss.php158
1 files changed, 71 insertions, 87 deletions
diff --git a/lib/lib_rss.php b/lib/lib_rss.php
index d1821b639..893bed8eb 100644
--- a/lib/lib_rss.php
+++ b/lib/lib_rss.php
@@ -4,8 +4,8 @@ if (version_compare(PHP_VERSION, FRESHRSS_MIN_PHP_VERSION, '<')) {
}
if (!function_exists('mb_strcut')) {
- function mb_strcut($str, $start, $length = null, $encoding = 'UTF-8') {
- return substr($str, $start, $length);
+ function mb_strcut(string $str, int $start, ?int $length = null, string $encoding = 'UTF-8'): string {
+ return substr($str, $start, $length) ?: '';
}
}
@@ -34,7 +34,7 @@ function join_path(...$path_parts): string {
}
//<Auto-loading>
-function classAutoloader($class) {
+function classAutoloader(string $class): void {
if (strpos($class, 'FreshRSS') === 0) {
$components = explode('_', $class);
switch (count($components)) {
@@ -73,14 +73,10 @@ function classAutoloader($class) {
spl_autoload_register('classAutoloader');
//</Auto-loading>
-/**
- * @param string $url
- * @return string
- */
-function idn_to_puny($url) {
+function idn_to_puny(string $url): string {
if (function_exists('idn_to_ascii')) {
$idn = parse_url($url, PHP_URL_HOST);
- if ($idn != '') {
+ if (is_string($idn) && $idn != '') {
// https://wiki.php.net/rfc/deprecate-and-remove-intl_idna_variant_2003
if (defined('INTL_IDNA_VARIANT_UTS46')) {
$puny = idn_to_ascii($idn, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
@@ -90,7 +86,7 @@ function idn_to_puny($url) {
$puny = idn_to_ascii($idn);
}
$pos = strpos($url, $idn);
- if ($puny != '' && $pos !== false) {
+ if ($puny != false && $pos !== false) {
$url = substr_replace($url, $puny, $pos, strlen($idn));
}
}
@@ -99,11 +95,9 @@ function idn_to_puny($url) {
}
/**
- * @param string $url
- * @param bool $fixScheme
* @return string|false
*/
-function checkUrl($url, $fixScheme = true) {
+function checkUrl(string $url, bool $fixScheme = true) {
$url = trim($url);
if ($url == '') {
return '';
@@ -127,31 +121,19 @@ function checkUrl($url, $fixScheme = true) {
* @return string
*/
function safe_ascii($text) {
- return filter_var($text, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
+ return filter_var($text, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) ?: '';
}
if (function_exists('mb_convert_encoding')) {
- /**
- * @param string $text
- * @return string
- */
- function safe_utf8($text) {
- return mb_convert_encoding($text, 'UTF-8', 'UTF-8');
+ function safe_utf8(string $text): string {
+ return mb_convert_encoding($text, 'UTF-8', 'UTF-8') ?: '';
}
} elseif (function_exists('iconv')) {
- /**
- * @param string $text
- * @return string
- */
- function safe_utf8($text) {
- return iconv('UTF-8', 'UTF-8//IGNORE', $text);
+ function safe_utf8(string $text): string {
+ return iconv('UTF-8', 'UTF-8//IGNORE', $text) ?: '';
}
} else {
- /**
- * @param string $text
- * @return string
- */
- function safe_utf8($text) {
+ function safe_utf8(string $text): string {
return $text;
}
}
@@ -178,14 +160,14 @@ function escapeToUnicodeAlternative($text, $extended = true) {
return trim(str_replace($problem, $replace, $text));
}
-function format_number($n, $precision = 0) {
+function format_number(float $n, int $precision = 0): string {
// number_format does not seem to be Unicode-compatible
return str_replace(' ', ' ', // Thin non-breaking space
number_format($n, $precision, '.', ' ')
);
}
-function format_bytes($bytes, $precision = 2, $system = 'IEC') {
+function format_bytes(int $bytes, int $precision = 2, string $system = 'IEC'): string {
if ($system === 'IEC') {
$base = 1024;
$units = array('B', 'KiB', 'MiB', 'GiB', 'TiB');
@@ -202,7 +184,7 @@ function format_bytes($bytes, $precision = 2, $system = 'IEC') {
return format_number($bytes, $precision) . ' ' . $units[$pow];
}
-function timestamptodate ($t, $hour = true) {
+function timestamptodate(int $t, bool $hour = true): string {
$month = _t('gen.date.' . date('M', $t));
if ($hour) {
$date = _t('gen.date.format_date_hour', $month);
@@ -210,14 +192,13 @@ function timestamptodate ($t, $hour = true) {
$date = _t('gen.date.format_date', $month);
}
- return @date ($date, $t);
+ return @date($date, $t) ?: '';
}
/**
* Decode HTML entities but preserve XML entities.
- * @param string|null $text
*/
-function html_only_entity_decode($text): string {
+function html_only_entity_decode(?string $text): string {
static $htmlEntitiesOnly = null;
if ($htmlEntitiesOnly === null) {
$htmlEntitiesOnly = array_flip(array_diff(
@@ -225,7 +206,7 @@ function html_only_entity_decode($text): string {
get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES, 'UTF-8') //Preserve XML entities
));
}
- return $text == '' ? '' : strtr($text, $htmlEntitiesOnly);
+ return $text == null ? '' : strtr($text, $htmlEntitiesOnly);
}
/**
@@ -239,8 +220,10 @@ function sensitive_log($log) {
foreach ($log as $k => $v) {
if (in_array($k, ['api_key', 'Passwd', 'T'])) {
$log[$k] = '██';
- } else {
+ } elseif (is_array($v) || is_string($v)) {
$log[$k] = sensitive_log($v);
+ } else {
+ return '';
}
}
} elseif (is_string($log)) {
@@ -248,7 +231,7 @@ function sensitive_log($log) {
'/\b(auth=.*?\/)[^&]+/i',
'/\b(Passwd=)[^&]+/i',
'/\b(Authorization)[^&]+/i',
- ], '$1█', $log);
+ ], '$1█', $log) ?? '';
}
return $log;
}
@@ -257,6 +240,9 @@ function sensitive_log($log) {
* @param array<string,mixed> $attributes
*/
function customSimplePie($attributes = array()): SimplePie {
+ if (FreshRSS_Context::$system_conf === null) {
+ throw new FreshRSS_Context_Exception('System configuration not initialised!');
+ }
$limits = FreshRSS_Context::$system_conf->limits;
$simplePie = new SimplePie();
$simplePie->set_useragent(FRESHRSS_USERAGENT);
@@ -338,13 +324,13 @@ function customSimplePie($attributes = array()): SimplePie {
}
/**
- * @param int|false $maxLength
+ * @param string $data
*/
-function sanitizeHTML($data, string $base = '', $maxLength = false) {
- if (!is_string($data) || ($maxLength !== false && $maxLength <= 0)) {
+function sanitizeHTML($data, string $base = '', ?int $maxLength = null): string {
+ if (!is_string($data) || ($maxLength !== null && $maxLength <= 0)) {
return '';
}
- if ($maxLength !== false) {
+ if ($maxLength !== null) {
$data = mb_strcut($data, 0, $maxLength, 'UTF-8');
}
static $simplePie = null;
@@ -353,7 +339,7 @@ function sanitizeHTML($data, string $base = '', $maxLength = false) {
$simplePie->init();
}
$result = html_only_entity_decode($simplePie->sanitize->sanitize($data, SIMPLEPIE_CONSTRUCT_HTML, $base));
- if ($maxLength !== false && strlen($result) > $maxLength) {
+ if ($maxLength !== null && strlen($result) > $maxLength) {
//Sanitizing has made the result too long so try again shorter
$data = mb_strcut($result, 0, (2 * $maxLength) - strlen($result) - 2, 'UTF-8');
return sanitizeHTML($data, $base, $maxLength);
@@ -361,9 +347,9 @@ function sanitizeHTML($data, string $base = '', $maxLength = false) {
return $result;
}
-function cleanCache(int $hours = 720) {
+function cleanCache(int $hours = 720): void {
// N.B.: GLOB_BRACE is not available on all platforms
- $files = array_merge(glob(CACHE_PATH . '/*.html', GLOB_NOSORT), glob(CACHE_PATH . '/*.spc', GLOB_NOSORT));
+ $files = array_merge(glob(CACHE_PATH . '/*.html', GLOB_NOSORT) ?: [], glob(CACHE_PATH . '/*.spc', GLOB_NOSORT) ?: []);
foreach ($files as $file) {
if (substr($file, -10) === 'index.html') {
continue;
@@ -412,13 +398,16 @@ function enforceHttpEncoding(string $html, string $contentType = ''): string {
* @param array<string,mixed> $attributes
*/
function httpGet(string $url, string $cachePath, string $type = 'html', array $attributes = []): string {
+ if (FreshRSS_Context::$system_conf === null) {
+ throw new FreshRSS_Context_Exception('System configuration not initialised!');
+ }
$limits = FreshRSS_Context::$system_conf->limits;
$feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']);
$cacheMtime = @filemtime($cachePath);
if ($cacheMtime !== false && $cacheMtime > time() - intval($limits['cache_duration'])) {
$body = @file_get_contents($cachePath);
- if ($body != '') {
+ if ($body != false) {
syslog(LOG_DEBUG, 'FreshRSS uses cache for ' . SimplePie_Misc::url_remove_credentials($url));
return $body;
}
@@ -472,7 +461,7 @@ function httpGet(string $url, string $cachePath, string $type = 'html', array $a
}
$body = curl_exec($ch);
$c_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- $c_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); //TODO: Check if that may be null
+ $c_content_type = '' . curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$c_error = curl_error($ch);
curl_close($ch);
@@ -481,7 +470,7 @@ function httpGet(string $url, string $cachePath, string $type = 'html', array $a
$body = '';
// TODO: Implement HTTP 410 Gone
}
- if ($body == false) {
+ if (!is_string($body)) {
$body = '';
} else {
$body = enforceHttpEncoding($body, $c_content_type);
@@ -498,10 +487,9 @@ function httpGet(string $url, string $cachePath, string $type = 'html', array $a
* Validate an email address, supports internationalized addresses.
*
* @param string $email The address to validate
- *
* @return bool true if email is valid, else false
*/
-function validateEmailAddress($email) {
+function validateEmailAddress(string $email): bool {
$mailer = new PHPMailer\PHPMailer\PHPMailer();
$mailer->CharSet = 'utf-8';
$punyemail = $mailer->punyencodeAddress($email);
@@ -512,9 +500,8 @@ function validateEmailAddress($email) {
* Add support of image lazy loading
* Move content from src attribute to data-original
* @param string $content is the text we want to parse
- * @return string
*/
-function lazyimg($content) {
+function lazyimg(string $content): string {
return preg_replace([
'/<((?:img|iframe)[^>]+?)src="([^"]+)"([^>]*)>/i',
"/<((?:img|iframe)[^>]+?)src='([^']+)'([^>]*)>/i",
@@ -523,18 +510,15 @@ function lazyimg($content) {
"<$1src='" . Minz_Url::display('/themes/icons/grey.gif') . "' data-original='$2'$3>",
],
$content
- );
+ ) ?? '';
}
-/**
- * @return string
- */
-function uTimeString() {
+function uTimeString(): string {
$t = @gettimeofday();
return $t['sec'] . str_pad('' . $t['usec'], 6, '0', STR_PAD_LEFT);
}
-function invalidateHttpCache($username = '') {
+function invalidateHttpCache(string $username = ''): bool {
if (!FreshRSS_user_Controller::checkUsername($username)) {
Minz_Session::_param('touch', uTimeString());
$username = Minz_Session::param('currentUser', '_');
@@ -549,12 +533,12 @@ function invalidateHttpCache($username = '') {
/**
* @return array<string>
*/
-function listUsers() {
+function listUsers(): array {
$final_list = array();
$base_path = join_path(DATA_PATH, 'users');
$dir_list = array_values(array_diff(
- scandir($base_path),
- array('..', '.', '_')
+ scandir($base_path) ?: [],
+ ['..', '.', '_']
));
foreach ($dir_list as $file) {
if ($file[0] !== '.' && is_dir(join_path($base_path, $file)) && file_exists(join_path($base_path, $file, 'config.php'))) {
@@ -567,12 +551,14 @@ function listUsers() {
/**
* Return if the maximum number of registrations has been reached.
- *
- * Note a max_regstrations of 0 means there is no limit.
+ * Note a max_registrations of 0 means there is no limit.
*
* @return boolean true if number of users >= max registrations, false else.
*/
-function max_registrations_reached() {
+function max_registrations_reached(): bool {
+ if (FreshRSS_Context::$system_conf === null) {
+ throw new FreshRSS_Context_Exception('System configuration not initialised!');
+ }
$limit_registrations = FreshRSS_Context::$system_conf->limits['max_registrations'];
$number_accounts = count(listUsers());
@@ -589,7 +575,7 @@ function max_registrations_reached() {
* @param string $username the name of the user of which we want the configuration.
* @return FreshRSS_UserConfiguration|null object, or null if the configuration cannot be loaded.
*/
-function get_user_configuration($username) {
+function get_user_configuration(string $username) {
if (!FreshRSS_user_Controller::checkUsername($username)) {
return null;
}
@@ -621,7 +607,7 @@ function get_user_configuration($username) {
*/
function ipToBits(string $ip): string {
$binaryip = '';
- foreach (str_split(inet_pton($ip)) as $char) {
+ foreach (str_split(inet_pton($ip) ?: '') as $char) {
$binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
}
return $binaryip;
@@ -654,6 +640,9 @@ function checkCIDR(string $ip, string $range): bool {
* @return boolean, true if the sender's IP is in one of the ranges defined in the configuration, else false
*/
function checkTrustedIP(): bool {
+ if (FreshRSS_Context::$system_conf === null) {
+ throw new FreshRSS_Context_Exception('System configuration not initialised!');
+ }
if (!empty($_SERVER['REMOTE_ADDR'])) {
foreach (FreshRSS_Context::$system_conf->trusted_sources as $cidr) {
if (checkCIDR($_SERVER['REMOTE_ADDR'], $cidr)) {
@@ -664,10 +653,7 @@ function checkTrustedIP(): bool {
return false;
}
-/**
- * @return string
- */
-function httpAuthUser() {
+function httpAuthUser(): string {
if (!empty($_SERVER['REMOTE_USER'])) {
return $_SERVER['REMOTE_USER'];
} elseif (!empty($_SERVER['HTTP_REMOTE_USER']) && checkTrustedIP()) {
@@ -680,10 +666,7 @@ function httpAuthUser() {
return '';
}
-/**
- * @return bool
- */
-function cryptAvailable() {
+function cryptAvailable(): bool {
try {
$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
return $hash === @crypt('password', $hash);
@@ -699,7 +682,7 @@ function cryptAvailable() {
*
* @return array<string,bool> of tested values.
*/
-function check_install_php() {
+function check_install_php(): array {
$pdo_mysql = extension_loaded('pdo_mysql');
$pdo_pgsql = extension_loaded('pdo_pgsql');
$pdo_sqlite = extension_loaded('pdo_sqlite');
@@ -723,7 +706,7 @@ function check_install_php() {
*
* @return array<string,bool> of tested values.
*/
-function check_install_files() {
+function check_install_files(): array {
return array(
// @phpstan-ignore-next-line
'data' => DATA_PATH && touch(DATA_PATH . '/index.html'), // is_writable() is not reliable for a folder on NFS
@@ -742,7 +725,7 @@ function check_install_files() {
*
* @return array<string,bool> of tested values.
*/
-function check_install_database() {
+function check_install_database(): array {
$status = array(
'connection' => true,
'tables' => false,
@@ -773,17 +756,14 @@ function check_install_database() {
/**
* Remove a directory recursively.
- *
* From http://php.net/rmdir#110489
- *
- * @param string $dir the directory to remove
*/
-function recursive_unlink($dir) {
+function recursive_unlink(string $dir): bool {
if (!is_dir($dir)) {
return true;
}
- $files = array_diff(scandir($dir), array('.', '..'));
+ $files = array_diff(scandir($dir) ?: [], ['.', '..']);
foreach ($files as $filename) {
$filename = $dir . '/' . $filename;
if (is_dir($filename)) {
@@ -803,7 +783,7 @@ function recursive_unlink($dir) {
* @param array<int,array<string,string>> $queries an array of queries.
* @return array<int,array<string,string>> without queries where $get is appearing.
*/
-function remove_query_by_get($get, $queries) {
+function remove_query_by_get(string $get, array $queries): array {
$final_queries = array();
foreach ($queries as $key => $query) {
if (empty($query['get']) || $query['get'] !== $get) {
@@ -827,7 +807,11 @@ const SHORTCUT_KEYS = [
'End', 'Enter', 'Escape', 'Home', 'Insert', 'PageDown', 'PageUp', 'Space', 'Tab',
];
-function getNonStandardShortcuts($shortcuts) {
+/**
+ * @param array<string> $shortcuts
+ * @return array<string>
+ */
+function getNonStandardShortcuts(array $shortcuts): array {
$standard = strtolower(implode(' ', SHORTCUT_KEYS));
$nonStandard = array_filter($shortcuts, function ($shortcut) use ($standard) {
@@ -838,7 +822,7 @@ function getNonStandardShortcuts($shortcuts) {
return $nonStandard;
}
-function errorMessageInfo($errorTitle, $error = '') {
+function errorMessageInfo(string $errorTitle, string $error = ''): string {
$errorTitle = htmlspecialchars($errorTitle, ENT_NOQUOTES, 'UTF-8');
$message = '';