Răsfoiți Sursa

Use typed access to request parameters (#5267)

* Use typed access to request parameters
This was a big source of mixed datatypes in many places

* Fix notifications

* Fix bookmarkAction
Alexandre Alapetite 3 ani în urmă
părinte
comite
6c01e4e7d6
37 a modificat fișierele cu 460 adăugiri și 426 ștergeri
  1. 1 2
      app/Controllers/apiController.php
  2. 17 19
      app/Controllers/authController.php
  3. 13 16
      app/Controllers/categoryController.php
  4. 57 58
      app/Controllers/configureController.php
  5. 11 11
      app/Controllers/entryController.php
  6. 69 54
      app/Controllers/feedController.php
  7. 13 15
      app/Controllers/importExportController.php
  8. 7 7
      app/Controllers/indexController.php
  9. 7 7
      app/Controllers/statsController.php
  10. 70 61
      app/Controllers/subscriptionController.php
  11. 14 17
      app/Controllers/tagController.php
  12. 1 1
      app/Controllers/updateController.php
  13. 37 37
      app/Controllers/userController.php
  14. 1 1
      app/FreshRSS.php
  15. 2 2
      app/Models/Auth.php
  16. 8 12
      app/Models/Context.php
  17. 1 1
      app/Models/SystemConfiguration.php
  18. 2 2
      app/Models/TagDAO.php
  19. 2 2
      app/Models/UserConfiguration.php
  20. 1 1
      app/Services/ExportService.php
  21. 4 4
      app/layout/aside_feed.phtml
  22. 6 6
      app/layout/header.phtml
  23. 1 1
      app/layout/layout.phtml
  24. 6 6
      app/layout/nav_menu.phtml
  25. 1 1
      app/views/entry/bookmark.phtml
  26. 5 5
      app/views/helpers/feed/update.phtml
  27. 1 1
      app/views/helpers/javascript_vars.phtml
  28. 1 1
      app/views/index/normal.phtml
  29. 1 1
      app/views/index/reader.phtml
  30. 1 1
      app/views/subscription/add.phtml
  31. 1 1
      app/views/subscription/feed.phtml
  32. 2 2
      docs/en/developers/Minz/index.md
  33. 2 2
      docs/fr/developers/Minz/index.md
  34. 1 1
      lib/Minz/Helper.php
  35. 80 51
      lib/Minz/Request.php
  36. 13 15
      lib/Minz/Url.php
  37. 0 1
      tests/phpstan-next.txt

+ 1 - 2
app/Controllers/apiController.php

@@ -47,8 +47,7 @@ class FreshRSS_api_Controller extends FreshRSS_ActionController {
 			Minz_Request::forward($return_url, true);
 		}
 
-		$apiPasswordPlain = Minz_Request::param('apiPasswordPlain', '', true);
-		$apiPasswordPlain = trim($apiPasswordPlain);
+		$apiPasswordPlain = Minz_Request::paramString('apiPasswordPlain', true);
 		if ($apiPasswordPlain == '') {
 			Minz_Request::forward($return_url, true);
 		}

+ 17 - 19
app/Controllers/authController.php

@@ -27,18 +27,16 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			$ok = true;
 
-			$anon = Minz_Request::param('anon_access', false);
-			$anon = ((bool)$anon) && ($anon !== 'no');
-			$anon_refresh = Minz_Request::param('anon_refresh', false);
-			$anon_refresh = ((bool)$anon_refresh) && ($anon_refresh !== 'no');
-			$auth_type = Minz_Request::param('auth_type', 'none');
-			$unsafe_autologin = Minz_Request::param('unsafe_autologin', false);
-			$api_enabled = Minz_Request::param('api_enabled', false);
-			if ($anon != FreshRSS_Context::$system_conf->allow_anonymous ||
-				$auth_type != FreshRSS_Context::$system_conf->auth_type ||
-				$anon_refresh != FreshRSS_Context::$system_conf->allow_anonymous_refresh ||
-				$unsafe_autologin != FreshRSS_Context::$system_conf->unsafe_autologin_enabled ||
-				$api_enabled != FreshRSS_Context::$system_conf->api_enabled) {
+			$anon = Minz_Request::paramBoolean('anon_access');
+			$anon_refresh = Minz_Request::paramBoolean('anon_refresh');
+			$auth_type = Minz_Request::paramString('auth_type') ?: 'none';
+			$unsafe_autologin = Minz_Request::paramBoolean('unsafe_autologin');
+			$api_enabled = Minz_Request::paramBoolean('api_enabled');
+			if ($anon !== FreshRSS_Context::$system_conf->allow_anonymous ||
+				$auth_type !== FreshRSS_Context::$system_conf->auth_type ||
+				$anon_refresh !== FreshRSS_Context::$system_conf->allow_anonymous_refresh ||
+				$unsafe_autologin !== FreshRSS_Context::$system_conf->unsafe_autologin_enabled ||
+				$api_enabled !== FreshRSS_Context::$system_conf->api_enabled) {
 
 				// TODO: test values from form
 				FreshRSS_Context::$system_conf->auth_type = $auth_type;
@@ -67,7 +65,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
 	 * the user is already connected.
 	 */
 	public function loginAction(): void {
-		if (FreshRSS_Auth::hasAccess() && Minz_Request::param('u', '') == '') {
+		if (FreshRSS_Auth::hasAccess() && Minz_Request::paramString('u') === '') {
 			Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
 		}
 
@@ -120,8 +118,8 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
 
 		if ($isPOST) {
 			$nonce = Minz_Session::param('nonce', '');
-			$username = Minz_Request::param('username', '');
-			$challenge = Minz_Request::param('challenge', '');
+			$username = Minz_Request::paramString('username');
+			$challenge = Minz_Request::paramString('challenge');
 
 			usleep(random_int(100, 10000));	//Primitive mitigation of timing attacks, in μs
 
@@ -152,7 +150,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
 				FreshRSS_Auth::giveAccess();
 
 				// Set cookie parameter if needed.
-				if (Minz_Request::param('keep_logged_in')) {
+				if (Minz_Request::paramBoolean('keep_logged_in')) {
 					FreshRSS_FormAuth::makeCookie($username, FreshRSS_Context::$user_conf->passwordHash);
 				} else {
 					FreshRSS_FormAuth::deleteCookie();
@@ -161,7 +159,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
 				Minz_Translate::init(FreshRSS_Context::$user_conf->language);
 
 				// All is good, go back to the original request or the index.
-				$url = Minz_Url::unserialize(Minz_Request::param('original_request'));
+				$url = Minz_Url::unserialize(Minz_Request::paramString('original_request'));
 				if (empty($url)) {
 					$url = [ 'c' => 'index', 'a' => 'index' ];
 				}
@@ -175,8 +173,8 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
 				Minz_Request::forward(['c' => 'auth', 'a' => 'login'], false);
 			}
 		} elseif (FreshRSS_Context::$system_conf->unsafe_autologin_enabled) {
-			$username = Minz_Request::param('u', '');
-			$password = Minz_Request::param('p', '');
+			$username = Minz_Request::paramString('u');
+			$password = Minz_Request::paramString('p');
 			Minz_Request::_param('p');
 
 			if (!$username) {

+ 13 - 16
app/Controllers/categoryController.php

@@ -42,8 +42,8 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			invalidateHttpCache();
 
-			$cat_name = trim(Minz_Request::param('new-category', ''));
-			if ($cat_name == '') {
+			$cat_name = Minz_Request::paramString('new-category');
+			if ($cat_name === '') {
 				Minz_Request::bad(_t('feedback.sub.category.no_name'), $url_redirect);
 			}
 
@@ -57,7 +57,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 				Minz_Request::bad(_t('feedback.tag.name_exists', $cat->name()), $url_redirect);
 			}
 
-			$opml_url = checkUrl(Minz_Request::param('opml_url', ''));
+			$opml_url = checkUrl(Minz_Request::paramString('opml_url'));
 			if ($opml_url != '') {
 				$cat->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML);
 				$cat->_attributes('opml_url', $opml_url);
@@ -91,8 +91,8 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			invalidateHttpCache();
 
-			$id = Minz_Request::param('id');
-			$name = Minz_Request::param('name', '');
+			$id = Minz_Request::paramInt('id');
+			$name = Minz_Request::paramString('name');
 			if (strlen($name) <= 0) {
 				Minz_Request::bad(_t('feedback.sub.category.no_name'), $url_redirect);
 			}
@@ -132,8 +132,8 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			invalidateHttpCache();
 
-			$id = Minz_Request::param('id');
-			if (!$id) {
+			$id = Minz_Request::paramInt('id');
+			if ($id === 0) {
 				Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect);
 			}
 
@@ -175,15 +175,12 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			invalidateHttpCache();
 
-			$id = Minz_Request::param('id');
-			if (!$id) {
+			$id = Minz_Request::paramInt('id');
+			if ($id === 0) {
 				Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect);
 			}
 
-			$muted = Minz_Request::param('muted', null);
-			if ($muted !== null) {
-				$muted = (bool)$muted;
-			}
+			$muted = Minz_Request::paramTernary('muted');
 
 			// List feeds to remove then related user queries.
 			$feeds = $feedDAO->listByCategory($id, $muted);
@@ -218,8 +215,8 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			invalidateHttpCache();
 
-			$id = Minz_Request::param('id');
-			if (!$id) {
+			$id = Minz_Request::paramInt('id');
+			if ($id === 0) {
 				Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect);
 			}
 
@@ -232,7 +229,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
 
 			$ok = $category->refreshDynamicOpml();
 
-			if (Minz_Request::param('ajax')) {
+			if (Minz_Request::paramBoolean('ajax')) {
 				Minz_Request::setGoodNotification(_t('feedback.sub.category.updated'));
 				$this->view->_layout(false);
 			} else {

+ 57 - 58
app/Controllers/configureController.php

@@ -42,27 +42,27 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 	 */
 	public function displayAction(): void {
 		if (Minz_Request::isPost()) {
-			FreshRSS_Context::$user_conf->language = Minz_Request::param('language', 'en');
-			FreshRSS_Context::$user_conf->timezone = Minz_Request::param('timezone', '');
-			FreshRSS_Context::$user_conf->theme = Minz_Request::param('theme', FreshRSS_Themes::$defaultTheme);
-			FreshRSS_Context::$user_conf->darkMode = Minz_Request::param('darkMode', 'no');
-			FreshRSS_Context::$user_conf->content_width = Minz_Request::param('content_width', 'thin');
-			FreshRSS_Context::$user_conf->topline_read = Minz_Request::param('topline_read', false);
-			FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::param('topline_favorite', false);
-			FreshRSS_Context::$user_conf->topline_date = Minz_Request::param('topline_date', false);
-			FreshRSS_Context::$user_conf->topline_link = Minz_Request::param('topline_link', false);
-			FreshRSS_Context::$user_conf->topline_website = Minz_Request::param('topline_website', false);
-			FreshRSS_Context::$user_conf->topline_thumbnail = Minz_Request::param('topline_thumbnail', false);
-			FreshRSS_Context::$user_conf->topline_summary = Minz_Request::param('topline_summary', false);
-			FreshRSS_Context::$user_conf->topline_display_authors = Minz_Request::param('topline_display_authors', false);
-			FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::param('bottomline_read', false);
-			FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::param('bottomline_favorite', false);
-			FreshRSS_Context::$user_conf->bottomline_sharing = Minz_Request::param('bottomline_sharing', false);
-			FreshRSS_Context::$user_conf->bottomline_tags = Minz_Request::param('bottomline_tags', false);
-			FreshRSS_Context::$user_conf->bottomline_date = Minz_Request::param('bottomline_date', false);
-			FreshRSS_Context::$user_conf->bottomline_link = Minz_Request::param('bottomline_link', false);
-			FreshRSS_Context::$user_conf->html5_notif_timeout = Minz_Request::param('html5_notif_timeout', 0);
-			FreshRSS_Context::$user_conf->show_nav_buttons = Minz_Request::param('show_nav_buttons', false);
+			FreshRSS_Context::$user_conf->language = Minz_Request::paramString('language') ?: 'en';
+			FreshRSS_Context::$user_conf->timezone = Minz_Request::paramString('timezone');
+			FreshRSS_Context::$user_conf->theme = Minz_Request::paramString('theme') ?: FreshRSS_Themes::$defaultTheme;
+			FreshRSS_Context::$user_conf->darkMode = Minz_Request::paramString('darkMode') ?: 'no';
+			FreshRSS_Context::$user_conf->content_width = Minz_Request::paramString('content_width') ?: 'thin';
+			FreshRSS_Context::$user_conf->topline_read = Minz_Request::paramBoolean('topline_read');
+			FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::paramBoolean('topline_favorite');
+			FreshRSS_Context::$user_conf->topline_date = Minz_Request::paramBoolean('topline_date');
+			FreshRSS_Context::$user_conf->topline_link = Minz_Request::paramBoolean('topline_link');
+			FreshRSS_Context::$user_conf->topline_website = Minz_Request::paramBoolean('topline_website');
+			FreshRSS_Context::$user_conf->topline_thumbnail = Minz_Request::paramBoolean('topline_thumbnail');
+			FreshRSS_Context::$user_conf->topline_summary = Minz_Request::paramBoolean('topline_summary');
+			FreshRSS_Context::$user_conf->topline_display_authors = Minz_Request::paramBoolean('topline_display_authors');
+			FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::paramBoolean('bottomline_read');
+			FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::paramBoolean('bottomline_favorite');
+			FreshRSS_Context::$user_conf->bottomline_sharing = Minz_Request::paramBoolean('bottomline_sharing');
+			FreshRSS_Context::$user_conf->bottomline_tags = Minz_Request::paramBoolean('bottomline_tags');
+			FreshRSS_Context::$user_conf->bottomline_date = Minz_Request::paramBoolean('bottomline_date');
+			FreshRSS_Context::$user_conf->bottomline_link = Minz_Request::paramBoolean('bottomline_link');
+			FreshRSS_Context::$user_conf->show_nav_buttons = Minz_Request::paramBoolean('show_nav_buttons');
+			FreshRSS_Context::$user_conf->html5_notif_timeout = Minz_Request::paramInt('html5_notif_timeout');
 			FreshRSS_Context::$user_conf->save();
 
 			Minz_Session::_param('language', FreshRSS_Context::$user_conf->language);
@@ -107,17 +107,17 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 	 */
 	public function readingAction(): void {
 		if (Minz_Request::isPost()) {
-			FreshRSS_Context::$user_conf->posts_per_page = Minz_Request::param('posts_per_page', 10);
-			FreshRSS_Context::$user_conf->view_mode = Minz_Request::param('view_mode', 'normal');
-			FreshRSS_Context::$user_conf->default_view = Minz_Request::param('default_view', 'adaptive');
+			FreshRSS_Context::$user_conf->posts_per_page = Minz_Request::paramInt('posts_per_page') ?: 10;
+			FreshRSS_Context::$user_conf->view_mode = Minz_Request::paramString('view_mode', true) ?: 'normal';
+			FreshRSS_Context::$user_conf->default_view = Minz_Request::paramString('default_view') ?: 'adaptive';
 			FreshRSS_Context::$user_conf->show_fav_unread = Minz_Request::paramBoolean('show_fav_unread');
 			FreshRSS_Context::$user_conf->auto_load_more = Minz_Request::paramBoolean('auto_load_more');
 			FreshRSS_Context::$user_conf->display_posts = Minz_Request::paramBoolean('display_posts');
-			FreshRSS_Context::$user_conf->display_categories = Minz_Request::param('display_categories', 'active');
-			FreshRSS_Context::$user_conf->show_tags = Minz_Request::param('show_tags', '0');
-			FreshRSS_Context::$user_conf->show_tags_max = Minz_Request::param('show_tags_max', '0');
-			FreshRSS_Context::$user_conf->show_author_date = Minz_Request::param('show_author_date', '0');
-			FreshRSS_Context::$user_conf->show_feed_name = Minz_Request::param('show_feed_name', 't');
+			FreshRSS_Context::$user_conf->display_categories = Minz_Request::paramString('display_categories') ?: 'active';
+			FreshRSS_Context::$user_conf->show_tags = Minz_Request::paramString('show_tags') ?: '0';
+			FreshRSS_Context::$user_conf->show_tags_max = Minz_Request::paramInt('show_tags_max');
+			FreshRSS_Context::$user_conf->show_author_date = Minz_Request::paramString('show_author_date') ?: '0';
+			FreshRSS_Context::$user_conf->show_feed_name = Minz_Request::paramString('show_feed_name') ?: 't';
 			FreshRSS_Context::$user_conf->hide_read_feeds = Minz_Request::paramBoolean('hide_read_feeds');
 			FreshRSS_Context::$user_conf->onread_jump_next = Minz_Request::paramBoolean('onread_jump_next');
 			FreshRSS_Context::$user_conf->lazyload = Minz_Request::paramBoolean('lazyload');
@@ -126,14 +126,14 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 			FreshRSS_Context::$user_conf->reading_confirm = Minz_Request::paramBoolean('reading_confirm');
 			FreshRSS_Context::$user_conf->auto_remove_article = Minz_Request::paramBoolean('auto_remove_article');
 			FreshRSS_Context::$user_conf->mark_updated_article_unread = Minz_Request::paramBoolean('mark_updated_article_unread');
-			FreshRSS_Context::$user_conf->sort_order = Minz_Request::param('sort_order', 'DESC');
+			FreshRSS_Context::$user_conf->sort_order = Minz_Request::paramString('sort_order') ?: 'DESC';
 			FreshRSS_Context::$user_conf->mark_when = array(
 				'article' => Minz_Request::paramBoolean('mark_open_article'),
 				'gone' => Minz_Request::paramBoolean('read_upon_gone'),
-				'max_n_unread' => Minz_Request::paramBoolean('enable_keep_max_n_unread') ? Minz_Request::param('keep_max_n_unread', false) : false,
+				'max_n_unread' => Minz_Request::paramBoolean('enable_keep_max_n_unread') ? Minz_Request::paramInt('keep_max_n_unread') : false,
 				'reception' => Minz_Request::paramBoolean('mark_upon_reception'),
 				'same_title_in_feed' => Minz_Request::paramBoolean('enable_read_when_same_title_in_feed') ?
-					Minz_Request::param('read_when_same_title_in_feed', false) : false,
+					Minz_Request::paramBoolean('read_when_same_title_in_feed') : false,
 				'scroll' => Minz_Request::paramBoolean('mark_scroll'),
 				'site' => Minz_Request::paramBoolean('mark_open_site'),
 			);
@@ -188,8 +188,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 		$this->view->list_keys = SHORTCUT_KEYS;
 
 		if (Minz_Request::isPost()) {
-			$shortcuts = Minz_Request::param('shortcuts');
-			if (false !== Minz_Request::param('load_default_shortcuts')) {
+			$shortcuts = Minz_Request::paramArray('shortcuts');
+			if (!Minz_Request::paramBoolean('load_default_shortcuts')) {
 				$default = Minz_Configuration::load(FRESHRSS_PATH . '/config-user.default.php');
 				$shortcuts = $default['shortcuts'];
 			}
@@ -217,25 +217,25 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 	 */
 	public function archivingAction(): void {
 		if (Minz_Request::isPost()) {
-			if (!Minz_Request::paramBoolean('enable_keep_max')) {
+			if (Minz_Request::paramBoolean('enable_keep_max')) {
+				$keepMax = Minz_Request::paramInt('keep_max') ?: FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
+			} else {
 				$keepMax = false;
-			} elseif (!$keepMax = Minz_Request::param('keep_max')) {
-				$keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
 			}
 			if (Minz_Request::paramBoolean('enable_keep_period')) {
 				$keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD;
-				if (is_numeric(Minz_Request::param('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::param('keep_period_unit'))) {
-					$keepPeriod = str_replace('1', Minz_Request::param('keep_period_count'), Minz_Request::param('keep_period_unit'));
+				if (is_numeric(Minz_Request::paramString('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::paramString('keep_period_unit'))) {
+					$keepPeriod = str_replace('1', Minz_Request::paramString('keep_period_count'), Minz_Request::paramString('keep_period_unit'));
 				}
 			} else {
 				$keepPeriod = false;
 			}
 
-			FreshRSS_Context::$user_conf->ttl_default = Minz_Request::param('ttl_default', FreshRSS_Feed::TTL_DEFAULT);
+			FreshRSS_Context::$user_conf->ttl_default = Minz_Request::paramInt('ttl_default') ?: FreshRSS_Feed::TTL_DEFAULT;
 			FreshRSS_Context::$user_conf->archiving = [
 				'keep_period' => $keepPeriod,
 				'keep_max' => $keepMax,
-				'keep_min' => Minz_Request::param('keep_min_default', 0),
+				'keep_min' => Minz_Request::paramInt('keep_min_default'),
 				'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'),
 				'keep_labels' => Minz_Request::paramBoolean('keep_labels'),
 				'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'),
@@ -294,7 +294,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 		$tag_dao = FreshRSS_Factory::createTagDao();
 
 		if (Minz_Request::isPost()) {
-			$params = Minz_Request::param('queries', array());
+			$params = Minz_Request::paramArray('queries');
 
 			$queries = [];
 			foreach ($params as $key => $query) {
@@ -321,10 +321,9 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 		$this->view->feeds = $feed_dao->listFeeds();
 		$this->view->tags = $tag_dao->listTags();
 
-		$id = Minz_Request::param('id');
+		$id = Minz_Request::paramInt('id');
 		$this->view->displaySlider = false;
-		if (false !== $id) {
-			$id = (int)$id;
+		if ($id !== 0) {
 			$this->view->displaySlider = true;
 			$this->view->query = $this->view->queries[$id];
 			$this->view->queryId = $id;
@@ -341,8 +340,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 	public function queryAction(): void {
 		$this->view->_layout(false);
 
-		$id = Minz_Request::param('id');
-		if (false === $id || !isset(FreshRSS_Context::$user_conf->queries[$id])) {
+		$id = Minz_Request::paramInt('id');
+		if ($id !== 0 || !isset(FreshRSS_Context::$user_conf->queries[$id])) {
 			Minz_Error::error(404);
 			return;
 		}
@@ -359,7 +358,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 		$this->view->tags = $tag_dao->listTags();
 
 		if (Minz_Request::isPost()) {
-			$params = array_filter(Minz_Request::param('query', []));
+			$params = array_filter(Minz_Request::paramArray('query'));
 			if (!empty($params['search'])) {
 				$params['search'] = htmlspecialchars_decode($params['search'], ENT_QUOTES);
 			}
@@ -367,7 +366,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 				$params['state'] = array_sum($params['state']);
 			}
 			$params['url'] = Minz_Url::display(['params' => $params]);
-			$name = Minz_Request::param('name', _t('conf.query.number', $id + 1));
+			$name = Minz_Request::paramString('name') ?: _t('conf.query.number', $id + 1);
 			if ('' === $name) {
 				$name = _t('conf.query.number', $id + 1);
 			}
@@ -388,8 +387,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 	 * Handles query deletion
 	 */
 	public function deleteQueryAction(): void {
-		$id = Minz_Request::param('id');
-		if (false === $id || !isset(FreshRSS_Context::$user_conf->queries[$id])) {
+		$id = Minz_Request::paramInt('id');
+		if ($id === 0 || empty(FreshRSS_Context::$user_conf->queries[$id])) {
 			Minz_Error::error(404);
 			return;
 		}
@@ -454,14 +453,14 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 
 		if (Minz_Request::isPost()) {
 			$limits = FreshRSS_Context::$system_conf->limits;
-			$limits['max_registrations'] = Minz_Request::param('max-registrations', 1);
-			$limits['max_feeds'] = Minz_Request::param('max-feeds', 16384);
-			$limits['max_categories'] = Minz_Request::param('max-categories', 16384);
-			$limits['cookie_duration'] = Minz_Request::param('cookie-duration', FreshRSS_Auth::DEFAULT_COOKIE_DURATION);
+			$limits['max_registrations'] = Minz_Request::paramInt('max-registrations') ?: 1;
+			$limits['max_feeds'] = Minz_Request::paramInt('max-feeds') ?: 16384;
+			$limits['max_categories'] = Minz_Request::paramInt('max-categories') ?: 16384;
+			$limits['cookie_duration'] = Minz_Request::paramInt('cookie-duration') ?: FreshRSS_Auth::DEFAULT_COOKIE_DURATION;
 			FreshRSS_Context::$system_conf->limits = $limits;
-			FreshRSS_Context::$system_conf->title = Minz_Request::param('instance-name', 'FreshRSS');
-			FreshRSS_Context::$system_conf->auto_update_url = Minz_Request::param('auto-update-url', false);
-			FreshRSS_Context::$system_conf->force_email_validation = Minz_Request::param('force-email-validation', false);
+			FreshRSS_Context::$system_conf->title = Minz_Request::paramString('instance-name') ?: 'FreshRSS';
+			FreshRSS_Context::$system_conf->auto_update_url = Minz_Request::paramString('auto-update-url');
+			FreshRSS_Context::$system_conf->force_email_validation = Minz_Request::paramBoolean('force-email-validation');
 			FreshRSS_Context::$system_conf->save();
 
 			invalidateHttpCache();

+ 11 - 11
app/Controllers/entryController.php

@@ -22,7 +22,7 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
 		}
 
 		// If ajax request, we do not print layout
-		$this->ajax = Minz_Request::param('ajax');
+		$this->ajax = Minz_Request::paramBoolean('ajax');
 		if ($this->ajax) {
 			$this->view->_layout(false);
 			Minz_Request::_param('ajax');
@@ -44,13 +44,13 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
 	 */
 	public function readAction(): void {
 		$id = Minz_Request::param('id');
-		$get = Minz_Request::param('get');
-		$next_get = Minz_Request::param('nextGet', $get);
-		$id_max = Minz_Request::param('idMax', 0);
-		$is_read = (bool)(Minz_Request::param('is_read', true));
-		FreshRSS_Context::$search = new FreshRSS_BooleanSearch(Minz_Request::param('search', ''));
+		$get = Minz_Request::paramString('get');
+		$next_get = Minz_Request::paramString('nextGet') ?: $get;
+		$id_max = Minz_Request::paramString('idMax') ?: '0';
+		$is_read = Minz_Request::paramTernary('is_read') ?? true;
+		FreshRSS_Context::$search = new FreshRSS_BooleanSearch(Minz_Request::paramString('search'));
 
-		FreshRSS_Context::$state = Minz_Request::param('state', 0);
+		FreshRSS_Context::$state = Minz_Request::paramInt('state');
 		if (FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_FAVORITE)) {
 			FreshRSS_Context::$state = FreshRSS_Entry::STATE_FAVORITE;
 		} elseif (FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_NOT_FAVORITE)) {
@@ -63,7 +63,7 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
 		$this->view->tags = array();
 
 		$entryDAO = FreshRSS_Factory::createEntryDao();
-		if ($id === false) {
+		if ($id == false) {
 			// id is false? It MUST be a POST request!
 			if (!Minz_Request::isPost()) {
 				Minz_Request::bad(_t('feedback.access.not_found'), array('c' => 'index', 'a' => 'index'));
@@ -134,9 +134,9 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
 	 * If id is false, nothing happened.
 	 */
 	public function bookmarkAction(): void {
-		$id = Minz_Request::param('id');
-		$is_favourite = (bool)Minz_Request::param('is_favorite', true);
-		if ($id !== false) {
+		$id = Minz_Request::paramString('id');
+		$is_favourite = Minz_Request::paramTernary('is_favorite') ?? true;
+		if ($id != '') {
 			$entryDAO = FreshRSS_Factory::createEntryDao();
 			$entryDAO->markFavorite($id, $is_favourite);
 		}

+ 69 - 54
app/Controllers/feedController.php

@@ -15,7 +15,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 			// and CRON task cannot be used with php command so the user can
 			// set a CRON task to refresh his feeds by using token inside url
 			$token = FreshRSS_Context::$user_conf->token;
-			$token_param = Minz_Request::param('token', '');
+			$token_param = Minz_Request::paramString('token');
 			$token_is_ok = ($token != '' && $token == $token_param);
 			$action = Minz_Request::actionName();
 			$allow_anonymous_refresh = FreshRSS_Context::$system_conf->allow_anonymous_refresh;
@@ -132,9 +132,9 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	 * If url_rss is false, nothing happened.
 	 */
 	public function addAction() {
-		$url = Minz_Request::param('url_rss');
+		$url = Minz_Request::paramString('url_rss');
 
-		if ($url === false) {
+		if ($url == '') {
 			// No url, do nothing
 			Minz_Request::forward(array(
 				'c' => 'subscription',
@@ -156,27 +156,27 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		}
 
 		if (Minz_Request::isPost()) {
-			$cat = Minz_Request::param('category');
+			$cat = Minz_Request::paramInt('category');
 
 			// HTTP information are useful if feed is protected behind a
 			// HTTP authentication
-			$user = trim(Minz_Request::param('http_user', ''));
-			$pass = trim(Minz_Request::param('http_pass', ''));
+			$user = Minz_Request::paramString('http_user');
+			$pass = Minz_Request::paramString('http_pass');
 			$http_auth = '';
 			if ($user != '' && $pass != '') {	//TODO: Sanitize
 				$http_auth = $user . ':' . $pass;
 			}
 
-			$cookie = Minz_Request::param('curl_params_cookie', '');
+			$cookie = Minz_Request::paramString('curl_params_cookie');
 			$cookie_file = Minz_Request::paramBoolean('curl_params_cookiefile');
-			$max_redirs = intval(Minz_Request::param('curl_params_redirects', 0));
-			$useragent = Minz_Request::param('curl_params_useragent', '');
-			$proxy_address = Minz_Request::param('curl_params', '');
-			$proxy_type = Minz_Request::param('proxy_type', '');
+			$max_redirs = Minz_Request::paramInt('curl_params_redirects');
+			$useragent = Minz_Request::paramString('curl_params_useragent');
+			$proxy_address = Minz_Request::paramString('curl_params');
+			$proxy_type = Minz_Request::paramString('proxy_type');
 			$opts = [];
 			if ($proxy_type !== '') {
 				$opts[CURLOPT_PROXY] = $proxy_address;
-				$opts[CURLOPT_PROXYTYPE] = intval($proxy_type);
+				$opts[CURLOPT_PROXYTYPE] = (int)$proxy_type;
 			}
 			if ($cookie !== '') {
 				$opts[CURLOPT_COOKIE] = $cookie;
@@ -186,7 +186,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 				// without reading any existing cookie data.
 				$opts[CURLOPT_COOKIEFILE] = '';
 			}
-			if ($max_redirs != 0) {
+			if ($max_redirs !== 0) {
 				$opts[CURLOPT_MAXREDIRS] = $max_redirs;
 				$opts[CURLOPT_FOLLOWLOCATION] = 1;
 			}
@@ -200,26 +200,36 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 				'curl_params' => empty($opts) ? null : $opts,
 			);
 			$attributes['ssl_verify'] = Minz_Request::paramTernary('ssl_verify');
-			$timeout = intval(Minz_Request::param('timeout', 0));
+			$timeout = Minz_Request::paramInt('timeout');
 			$attributes['timeout'] = $timeout > 0 ? $timeout : null;
 
-			$feed_kind = (int)Minz_Request::param('feed_kind', FreshRSS_Feed::KIND_RSS);
+			$feed_kind = Minz_Request::paramInt('feed_kind') ?: FreshRSS_Feed::KIND_RSS;
 			if ($feed_kind === FreshRSS_Feed::KIND_HTML_XPATH || $feed_kind === FreshRSS_Feed::KIND_XML_XPATH) {
 				$xPathSettings = [];
-				if (Minz_Request::param('xPathFeedTitle', '') != '') $xPathSettings['feedTitle'] = Minz_Request::param('xPathFeedTitle', '', true);
-				if (Minz_Request::param('xPathItem', '') != '') $xPathSettings['item'] = Minz_Request::param('xPathItem', '', true);
-				if (Minz_Request::param('xPathItemTitle', '') != '') $xPathSettings['itemTitle'] = Minz_Request::param('xPathItemTitle', '', true);
-				if (Minz_Request::param('xPathItemContent', '') != '') $xPathSettings['itemContent'] = Minz_Request::param('xPathItemContent', '', true);
-				if (Minz_Request::param('xPathItemUri', '') != '') $xPathSettings['itemUri'] = Minz_Request::param('xPathItemUri', '', true);
-				if (Minz_Request::param('xPathItemAuthor', '') != '') $xPathSettings['itemAuthor'] = Minz_Request::param('xPathItemAuthor', '', true);
-				if (Minz_Request::param('xPathItemTimestamp', '') != '') $xPathSettings['itemTimestamp'] = Minz_Request::param('xPathItemTimestamp', '', true);
-				if (Minz_Request::param('xPathItemTimeFormat', '') != '') $xPathSettings['itemTimeFormat'] = Minz_Request::param('xPathItemTimeFormat', '', true);
-				if (Minz_Request::param('xPathItemThumbnail', '') != '') $xPathSettings['itemThumbnail'] = Minz_Request::param('xPathItemThumbnail', '', true);
-				if (Minz_Request::param('xPathItemCategories', '') != '') $xPathSettings['itemCategories'] = Minz_Request::param('xPathItemCategories', '', true);
-				if (Minz_Request::param('xPathItemUid', '') != '') $xPathSettings['itemUid'] = Minz_Request::param('xPathItemUid', '', true);
-				if (!empty($xPathSettings)) {
+				if (Minz_Request::paramString('xPathFeedTitle') != '')
+					$xPathSettings['feedTitle'] = Minz_Request::paramString('xPathFeedTitle', true);
+				if (Minz_Request::paramString('xPathItem') != '')
+					$xPathSettings['item'] = Minz_Request::paramString('xPathItem', true);
+				if (Minz_Request::paramString('xPathItemTitle') != '')
+					$xPathSettings['itemTitle'] = Minz_Request::paramString('xPathItemTitle', true);
+				if (Minz_Request::paramString('xPathItemContent') != '')
+					$xPathSettings['itemContent'] = Minz_Request::paramString('xPathItemContent', true);
+				if (Minz_Request::paramString('xPathItemUri') != '')
+					$xPathSettings['itemUri'] = Minz_Request::paramString('xPathItemUri', true);
+				if (Minz_Request::paramString('xPathItemAuthor') != '')
+					$xPathSettings['itemAuthor'] = Minz_Request::paramString('xPathItemAuthor', true);
+				if (Minz_Request::paramString('xPathItemTimestamp') != '')
+					$xPathSettings['itemTimestamp'] = Minz_Request::paramString('xPathItemTimestamp', true);
+				if (Minz_Request::paramString('xPathItemTimeFormat') != '')
+					$xPathSettings['itemTimeFormat'] = Minz_Request::paramString('xPathItemTimeFormat', true);
+				if (Minz_Request::paramString('xPathItemThumbnail') != '')
+					$xPathSettings['itemThumbnail'] = Minz_Request::paramString('xPathItemThumbnail', true);
+				if (Minz_Request::paramString('xPathItemCategories') != '')
+					$xPathSettings['itemCategories'] = Minz_Request::paramString('xPathItemCategories', true);
+				if (Minz_Request::paramString('xPathItemUid') != '')
+					$xPathSettings['itemUid'] = Minz_Request::paramString('xPathItemUid', true);
+				if (!empty($xPathSettings))
 					$attributes['xpath'] = $xPathSettings;
-				}
 			}
 
 			try {
@@ -227,19 +237,24 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 			} catch (FreshRSS_BadUrl_Exception $e) {
 				// Given url was not a valid url!
 				Minz_Log::warning($e->getMessage());
-				return Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect);
+				Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect);
+				return;
 			} catch (FreshRSS_Feed_Exception $e) {
 				// Something went bad (timeout, server not found, etc.)
 				Minz_Log::warning($e->getMessage());
-				return Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
+				Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
+				return;
 			} catch (Minz_FileNotExistException $e) {
 				// Cache directory doesn’t exist!
 				Minz_Log::error($e->getMessage());
-				return Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
+				Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
+				return;
 			} catch (FreshRSS_AlreadySubscribed_Exception $e) {
-				return Minz_Request::bad(_t('feedback.sub.feed.already_subscribed', $e->feedName()), $url_redirect);
+				Minz_Request::bad(_t('feedback.sub.feed.already_subscribed', $e->feedName()), $url_redirect);
+				return;
 			} catch (FreshRSS_FeedNotAdded_Exception $e) {
-				return Minz_Request::bad(_t('feedback.sub.feed.not_added', $e->url()), $url_redirect);
+				Minz_Request::bad(_t('feedback.sub.feed.not_added', $e->url()), $url_redirect);
+				return;
 			}
 
 			// Entries are in DB, we redirect to feed configuration page.
@@ -280,7 +295,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	 *   - id (default: false)
 	 */
 	public function truncateAction() {
-		$id = Minz_Request::param('id');
+		$id = Minz_Request::paramInt('id');
 		$url_redirect = array(
 			'c' => 'subscription',
 			'a' => 'index',
@@ -631,10 +646,10 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	 */
 	public function actualizeAction() {
 		Minz_Session::_param('actualize_feeds', false);
-		$id = Minz_Request::param('id');
-		$url = Minz_Request::param('url');
-		$force = Minz_Request::param('force');
-		$maxFeeds = (int)Minz_Request::param('maxFeeds');
+		$id = Minz_Request::paramInt('id');
+		$url = Minz_Request::paramString('url');
+		$force = Minz_Request::paramBoolean('force');
+		$maxFeeds = Minz_Request::paramInt('maxFeeds');
 		$noCommit = ($_POST['noCommit'] ?? 0) == 1;
 		$feed = null;
 
@@ -654,7 +669,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 			list($updated_feeds, $feed, $nb_new_articles) = self::actualizeFeed($id, $url, $force, null, $noCommit, $maxFeeds);
 		}
 
-		if (Minz_Request::param('ajax')) {
+		if (Minz_Request::paramBoolean('ajax')) {
 			// Most of the time, ajax request is for only one feed. But since
 			// there are several parallel requests, we should return that there
 			// are several updated feeds.
@@ -676,7 +691,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		return $updated_feeds;
 	}
 
-	public static function renameFeed($feed_id, $feed_name) {
+	public static function renameFeed(int $feed_id, string $feed_name) {
 		if ($feed_id <= 0 || $feed_name == '') {
 			return false;
 		}
@@ -685,7 +700,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		return $feedDAO->updateFeed($feed_id, array('name' => $feed_name));
 	}
 
-	public static function moveFeed($feed_id, $cat_id, $new_cat_name = '') {
+	public static function moveFeed(int $feed_id, int $cat_id, string $new_cat_name = '') {
 		if ($feed_id <= 0 || ($cat_id <= 0 && $new_cat_name == '')) {
 			return false;
 		}
@@ -725,8 +740,8 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 			Minz_Request::forward(array('c' => 'subscription'), true);
 		}
 
-		$feed_id = Minz_Request::param('f_id');
-		$cat_id = Minz_Request::param('c_id');
+		$feed_id = Minz_Request::paramInt('f_id');
+		$cat_id = Minz_Request::paramInt('c_id');
 
 		if (self::moveFeed($feed_id, $cat_id)) {
 			// TODO: return something useful
@@ -768,15 +783,15 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	 * @todo handle "r" redirection in Minz_Request::forward()?
 	 */
 	public function deleteAction() {
-		$from = Minz_Request::param('from');
-		$id = Minz_Request::param('id');
+		$from = Minz_Request::paramString('from');
+		$id = Minz_Request::paramInt('id');
 
 		switch ($from) {
 			case 'stats':
 				$redirect_url = array('c' => 'stats', 'a' => 'idle');
 				break;
 			case 'normal':
-				$get = Minz_Request::param('get');
+				$get = Minz_Request::paramString('get');
 				if ($get) {
 					$redirect_url = array('c' => 'index', 'a' => 'normal', 'params' => array('get' => $get));
 				} else {
@@ -784,8 +799,8 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 				}
 				break;
 			default:
-				$redirect_url = Minz_Request::param('r', false, true);
-				if (!$redirect_url) {
+				$redirect_url = Minz_Request::paramString('r', true);
+				if ($redirect_url !== '') {
 					$redirect_url = array('c' => 'subscription', 'a' => 'index');
 				}
 				if (!Minz_Request::isPost()) {
@@ -809,7 +824,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 	 */
 	public function clearCacheAction() {
 		//Get Feed.
-		$id = Minz_Request::param('id');
+		$id = Minz_Request::paramInt('id');
 
 		$feedDAO = FreshRSS_Factory::createFeedDao();
 		$feed = $feedDAO->searchById($id);
@@ -837,8 +852,8 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		@set_time_limit(300);
 
 		//Get Feed ID.
-		$feed_id = intval(Minz_Request::param('id', 0));
-		$limit = intval(Minz_Request::param('reload_limit', 10));
+		$feed_id = Minz_Request::paramInt('id');
+		$limit = Minz_Request::paramInt('reload_limit') ?: 10;
 
 		$feedDAO = FreshRSS_Factory::createFeedDao();
 		$entryDAO = FreshRSS_Factory::createEntryDao();
@@ -907,8 +922,8 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		]);
 
 		//Get parameters.
-		$feed_id = (int)(Minz_Request::param('id', 0));
-		$content_selector = trim(Minz_Request::param('selector'));
+		$feed_id = Minz_Request::paramInt('id');
+		$content_selector = Minz_Request::paramString('selector');
 
 		if (!$content_selector) {
 			$this->view->fatalError = _t('feedback.sub.feed.selector_preview.selector_empty');
@@ -941,7 +956,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 		}
 
 		$attributes = $feed->attributes();
-		$attributes['path_entries_filter'] = trim(Minz_Request::param('selector_filter', '', true));
+		$attributes['path_entries_filter'] = Minz_Request::paramString('selector_filter', true);
 
 		//Fetch & select content.
 		try {

+ 13 - 15
app/Controllers/importExportController.php

@@ -580,19 +580,18 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 	 */
 	public function exportAction() {
 		if (!Minz_Request::isPost()) {
-			return Minz_Request::forward(
-				array('c' => 'importExport', 'a' => 'index'),
-				true
-			);
+			Minz_Request::forward(['c' => 'importExport', 'a' => 'index'], true);
+			return;
 		}
 
 		$username = Minz_User::name();
 		$export_service = new FreshRSS_Export_Service($username);
 
-		$export_opml = Minz_Request::param('export_opml', false);
-		$export_starred = Minz_Request::param('export_starred', false);
-		$export_labelled = Minz_Request::param('export_labelled', false);
-		$export_feeds = Minz_Request::param('export_feeds', array());
+		$export_opml = Minz_Request::paramBoolean('export_opml');
+		$export_starred = Minz_Request::paramBoolean('export_starred');
+		$export_labelled = Minz_Request::paramBoolean('export_labelled');
+		/** @var array<numeric-string> */
+		$export_feeds = Minz_Request::paramArray('export_feeds');
 		$max_number_entries = 50;
 
 		$exported_files = [];
@@ -616,7 +615,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 		}
 
 		foreach ($export_feeds as $feed_id) {
-			$result = $export_service->generateFeedEntries($feed_id, $max_number_entries);
+			$result = $export_service->generateFeedEntries((int)$feed_id, $max_number_entries);
 			if (!$result) {
 				// It means the actual feed_id doesn’t correspond to any existing feed
 				continue;
@@ -629,10 +628,8 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 		$nb_files = count($exported_files);
 		if ($nb_files <= 0) {
 			// There’s nothing to do, there’re no files to export
-			return Minz_Request::forward(
-				array('c' => 'importExport', 'a' => 'index'),
-				true
-			);
+			Minz_Request::forward(['c' => 'importExport', 'a' => 'index'], true);
+			return;
 		}
 
 		if ($nb_files === 1) {
@@ -643,10 +640,11 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 			// More files? Let’s compress them in a Zip archive
 			if (!extension_loaded('zip')) {
 				// Oops, there is no ZIP extension!
-				return Minz_Request::bad(
+				Minz_Request::bad(
 					_t('feedback.import_export.export_no_zip_extension'),
-					array('c' => 'importExport', 'a' => 'index')
+					['c' => 'importExport', 'a' => 'index']
 				);
+				return;
 			}
 
 			[$filename, $content] = $export_service->zip($exported_files);

+ 7 - 7
app/Controllers/indexController.php

@@ -26,10 +26,10 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 			return;
 		}
 
-		$id = Minz_Request::param('id');
-		if ($id) {
-			$view = Minz_Request::param('a');
-			$url_redirect = array('c' => 'subscription', 'a' => 'feed', 'params' => array('id' => $id, 'from' => $view));
+		$id = Minz_Request::paramInt('id');
+		if ($id !== 0) {
+			$view = Minz_Request::paramString('a');
+			$url_redirect = array('c' => 'subscription', 'a' => 'feed', 'params' => array('id' => (string)$id, 'from' => $view));
 			Minz_Request::forward($url_redirect, true);
 			return;
 		}
@@ -144,7 +144,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 	public function rssAction(): void {
 		$allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous;
 		$token = FreshRSS_Context::$user_conf->token;
-		$token_param = Minz_Request::param('token', '');
+		$token_param = Minz_Request::paramString('token');
 		$token_is_ok = ($token != '' && $token === $token_param);
 
 		// Check if user has access.
@@ -177,7 +177,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 	public function opmlAction(): void {
 		$allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous;
 		$token = FreshRSS_Context::$user_conf->token;
-		$token_param = Minz_Request::param('token', '');
+		$token_param = Minz_Request::paramString('token');
 		$token_is_ok = ($token != '' && $token === $token_param);
 
 		// Check if user has access.
@@ -316,7 +316,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 		$logs = FreshRSS_LogDAO::lines();	//TODO: ask only the necessary lines
 
 		//gestion pagination
-		$page = intval(Minz_Request::param('page', 1));
+		$page = Minz_Request::paramInt('page') ?: 1;
 		$this->view->logsPaginator = new Minz_Paginator($logs);
 		$this->view->logsPaginator->_nbItemsPerPage(50);
 		$this->view->logsPaginator->_currentPage($page);

+ 7 - 7
app/Controllers/statsController.php

@@ -86,12 +86,12 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
 	 * but shows the stats idle page
 	 */
 	public function feedAction(): void {
-		$id = '' . Minz_Request::param('id', '');
-		$ajax = '' . Minz_Request::param('ajax', '');
+		$id = Minz_Request::paramInt('id');
+		$ajax = Minz_Request::paramBoolean('ajax');
 		if ($ajax) {
-			$url_redirect = array('c' => 'subscription', 'a' => 'feed', 'params' => array('id' => $id, 'from' => 'stats', 'ajax' => $ajax));
+			$url_redirect = array('c' => 'subscription', 'a' => 'feed', 'params' => array('id' => (string)$id, 'from' => 'stats', 'ajax' => (string)$ajax));
 		} else {
-			$url_redirect = array('c' => 'subscription', 'a' => 'feed', 'params' => array('id' => $id, 'from' => 'stats'));
+			$url_redirect = array('c' => 'subscription', 'a' => 'feed', 'params' => array('id' => (string)$id, 'from' => 'stats'));
 		}
 		Minz_Request::forward($url_redirect, true);
 	}
@@ -174,9 +174,9 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
 		$this->view->idleFeeds = $idleFeeds;
 		$this->view->feeds = $feed_dao->listFeeds();
 
-		$id = Minz_Request::param('id');
+		$id = Minz_Request::paramInt('id');
 		$this->view->displaySlider = false;
-		if (false !== $id) {
+		if ($id !== 0) {
 			$this->view->displaySlider = true;
 			$feedDAO = FreshRSS_Factory::createFeedDao();
 			$this->view->feed = $feedDAO->searchById($id);
@@ -202,7 +202,7 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
 
 		FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
 
-		$id = (int)(Minz_Request::param('id'));
+		$id = Minz_Request::paramInt('id');
 		if ($id === 0) {
 			$id = null;
 		}

+ 70 - 61
app/Controllers/subscriptionController.php

@@ -50,10 +50,10 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 
 		$this->view->onlyFeedsWithError = Minz_Request::paramTernary('error');
 
-		$id = Minz_Request::param('id');
+		$id = Minz_Request::paramInt('id');
 		$this->view->displaySlider = false;
-		if (false !== $id) {
-			$type = Minz_Request::param('type');
+		if ($id !== 0) {
+			$type = Minz_Request::paramString('type');
 			$this->view->displaySlider = true;
 			switch ($type) {
 				case 'category':
@@ -89,7 +89,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 	 * Default values are empty strings unless specified.
 	 */
 	public function feedAction(): void {
-		if (Minz_Request::param('ajax')) {
+		if (Minz_Request::paramBoolean('ajax')) {
 			$this->view->_layout(false);
 		} else {
 			FreshRSS_View::appendScript(Minz_Url::display('/scripts/feed.js?' . @filemtime(PUBLIC_PATH . '/scripts/feed.js')));
@@ -98,8 +98,8 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 		$feedDAO = FreshRSS_Factory::createFeedDao();
 		$this->view->feeds = $feedDAO->listFeeds();
 
-		$id = Minz_Request::param('id');
-		if ($id === false || !isset($this->view->feeds[$id])) {
+		$id = Minz_Request::paramInt('id');
+		if ($id === 0 || !isset($this->view->feeds[$id])) {
 			Minz_Error::error(404);
 			return;
 		}
@@ -110,26 +110,26 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 		FreshRSS_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $feed->name() . ' · ');
 
 		if (Minz_Request::isPost()) {
-			$user = trim(Minz_Request::param('http_user_feed' . $id, ''));
-			$pass = trim(Minz_Request::param('http_pass_feed' . $id, ''));
+			$user = Minz_Request::paramString('http_user_feed' . $id);
+			$pass = Minz_Request::paramString('http_pass_feed' . $id);
 
 			$httpAuth = '';
 			if ($user !== '' && $pass !== '') {	//TODO: Sanitize
 				$httpAuth = $user . ':' . $pass;
 			}
 
-			$feed->_ttl((int)Minz_Request::param('ttl', FreshRSS_Feed::TTL_DEFAULT));
-			$feed->_mute((bool)Minz_Request::param('mute', false));
+			$feed->_ttl(Minz_Request::paramInt('ttl') ?: FreshRSS_Feed::TTL_DEFAULT);
+			$feed->_mute(Minz_Request::paramBoolean('mute'));
 
 			$feed->_attributes('read_upon_gone', Minz_Request::paramTernary('read_upon_gone'));
 			$feed->_attributes('mark_updated_article_unread', Minz_Request::paramTernary('mark_updated_article_unread'));
 			$feed->_attributes('read_upon_reception', Minz_Request::paramTernary('read_upon_reception'));
 			$feed->_attributes('clear_cache', Minz_Request::paramTernary('clear_cache'));
 
-			$keep_max_n_unread = (int)Minz_Request::param('keep_max_n_unread', 0);
+			$keep_max_n_unread = Minz_Request::paramInt('keep_max_n_unread');
 			$feed->_attributes('keep_max_n_unread', $keep_max_n_unread > 0 ? $keep_max_n_unread : null);
 
-			$read_when_same_title_in_feed = Minz_Request::param('read_when_same_title_in_feed', '');
+			$read_when_same_title_in_feed = Minz_Request::paramString('read_when_same_title_in_feed');
 			if ($read_when_same_title_in_feed === '') {
 				$read_when_same_title_in_feed = null;
 			} else {
@@ -140,12 +140,12 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 			}
 			$feed->_attributes('read_when_same_title_in_feed', $read_when_same_title_in_feed);
 
-			$cookie = Minz_Request::param('curl_params_cookie', '');
+			$cookie = Minz_Request::paramString('curl_params_cookie');
 			$cookie_file = Minz_Request::paramBoolean('curl_params_cookiefile');
-			$max_redirs = (int)Minz_Request::param('curl_params_redirects', 0);
-			$useragent = Minz_Request::param('curl_params_useragent', '');
-			$proxy_address = Minz_Request::param('curl_params', '');
-			$proxy_type = Minz_Request::param('proxy_type', '');
+			$max_redirs = Minz_Request::paramInt('curl_params_redirects');
+			$useragent = Minz_Request::paramString('curl_params_useragent');
+			$proxy_address = Minz_Request::paramString('curl_params');
+			$proxy_type = Minz_Request::paramString('proxy_type');
 			$opts = [];
 			if ($proxy_type !== '') {
 				$opts[CURLOPT_PROXY] = $proxy_address;
@@ -168,24 +168,24 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 			}
 			$feed->_attributes('curl_params', empty($opts) ? null : $opts);
 
-			$feed->_attributes('content_action', Minz_Request::param('content_action', 'replace', true));
+			$feed->_attributes('content_action', Minz_Request::paramString('content_action', true) ?: 'replace');
 
 			$feed->_attributes('ssl_verify', Minz_Request::paramTernary('ssl_verify'));
-			$timeout = (int)Minz_Request::param('timeout', 0);
+			$timeout = Minz_Request::paramInt('timeout');
 			$feed->_attributes('timeout', $timeout > 0 ? $timeout : null);
 
 			if (Minz_Request::paramBoolean('use_default_purge_options')) {
 				$feed->_attributes('archiving', null);
 			} else {
-				if (!Minz_Request::paramBoolean('enable_keep_max')) {
+				if (Minz_Request::paramBoolean('enable_keep_max')) {
+					$keepMax = Minz_Request::paramInt('keep_max') ?: FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
+				} else {
 					$keepMax = false;
-				} elseif (!$keepMax = Minz_Request::param('keep_max')) {
-					$keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
 				}
-				if ($enableRetentionPeriod = Minz_Request::paramBoolean('enable_keep_period')) {
+				if (Minz_Request::paramBoolean('enable_keep_period')) {
 					$keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD;
-					if (is_numeric(Minz_Request::param('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::param('keep_period_unit'))) {
-						$keepPeriod = str_replace('1', Minz_Request::param('keep_period_count'), Minz_Request::param('keep_period_unit'));
+					if (is_numeric(Minz_Request::paramString('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::paramString('keep_period_unit'))) {
+						$keepPeriod = str_replace('1', Minz_Request::paramString('keep_period_count'), Minz_Request::paramString('keep_period_unit'));
 					}
 				} else {
 					$keepPeriod = false;
@@ -193,44 +193,53 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 				$feed->_attributes('archiving', [
 					'keep_period' => $keepPeriod,
 					'keep_max' => $keepMax,
-					'keep_min' => (int)Minz_Request::param('keep_min', 0),
+					'keep_min' => Minz_Request::paramInt('keep_min'),
 					'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'),
 					'keep_labels' => Minz_Request::paramBoolean('keep_labels'),
 					'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'),
 				]);
 			}
 
-			$feed->_filtersAction('read', preg_split('/[\n\r]+/', Minz_Request::param('filteractions_read', '')));
+			$feed->_filtersAction('read', preg_split('/[\n\r]+/', Minz_Request::paramString('filteractions_read')));
 
-			$feed->_kind((int)Minz_Request::param('feed_kind', FreshRSS_Feed::KIND_RSS));
+			$feed->_kind(Minz_Request::paramInt('feed_kind') ?: FreshRSS_Feed::KIND_RSS);
 			if ($feed->kind() === FreshRSS_Feed::KIND_HTML_XPATH || $feed->kind() === FreshRSS_Feed::KIND_XML_XPATH) {
 				$xPathSettings = [];
-				if (Minz_Request::param('xPathItem', '') != '') $xPathSettings['item'] = Minz_Request::param('xPathItem', '', true);
-				if (Minz_Request::param('xPathItemTitle', '') != '') $xPathSettings['itemTitle'] = Minz_Request::param('xPathItemTitle', '', true);
-				if (Minz_Request::param('xPathItemContent', '') != '') $xPathSettings['itemContent'] = Minz_Request::param('xPathItemContent', '', true);
-				if (Minz_Request::param('xPathItemUri', '') != '') $xPathSettings['itemUri'] = Minz_Request::param('xPathItemUri', '', true);
-				if (Minz_Request::param('xPathItemAuthor', '') != '') $xPathSettings['itemAuthor'] = Minz_Request::param('xPathItemAuthor', '', true);
-				if (Minz_Request::param('xPathItemTimestamp', '') != '') $xPathSettings['itemTimestamp'] = Minz_Request::param('xPathItemTimestamp', '', true);
-				if (Minz_Request::param('xPathItemTimeFormat', '') != '') $xPathSettings['itemTimeFormat'] = Minz_Request::param('xPathItemTimeFormat', '', true);
-				if (Minz_Request::param('xPathItemThumbnail', '') != '') $xPathSettings['itemThumbnail'] = Minz_Request::param('xPathItemThumbnail', '', true);
-				if (Minz_Request::param('xPathItemCategories', '') != '') $xPathSettings['itemCategories'] = Minz_Request::param('xPathItemCategories', '', true);
-				if (Minz_Request::param('xPathItemUid', '') != '') $xPathSettings['itemUid'] = Minz_Request::param('xPathItemUid', '', true);
-				if (!empty($xPathSettings)) {
+				if (Minz_Request::paramString('xPathItem') != '')
+					$xPathSettings['item'] = Minz_Request::paramString('xPathItem', true);
+				if (Minz_Request::paramString('xPathItemTitle') != '')
+					$xPathSettings['itemTitle'] = Minz_Request::paramString('xPathItemTitle', true);
+				if (Minz_Request::paramString('xPathItemContent') != '')
+					$xPathSettings['itemContent'] = Minz_Request::paramString('xPathItemContent', true);
+				if (Minz_Request::paramString('xPathItemUri') != '')
+					$xPathSettings['itemUri'] = Minz_Request::paramString('xPathItemUri', true);
+				if (Minz_Request::paramString('xPathItemAuthor') != '')
+					$xPathSettings['itemAuthor'] = Minz_Request::paramString('xPathItemAuthor', true);
+				if (Minz_Request::paramString('xPathItemTimestamp') != '')
+					$xPathSettings['itemTimestamp'] = Minz_Request::paramString('xPathItemTimestamp', true);
+				if (Minz_Request::paramString('xPathItemTimeFormat') != '')
+					$xPathSettings['itemTimeFormat'] = Minz_Request::paramString('xPathItemTimeFormat', true);
+				if (Minz_Request::paramString('xPathItemThumbnail') != '')
+					$xPathSettings['itemThumbnail'] = Minz_Request::paramString('xPathItemThumbnail', true);
+				if (Minz_Request::paramString('xPathItemCategories') != '')
+					$xPathSettings['itemCategories'] = Minz_Request::paramString('xPathItemCategories', true);
+				if (Minz_Request::paramString('xPathItemUid') != '')
+					$xPathSettings['itemUid'] = Minz_Request::paramString('xPathItemUid', true);
+				if (!empty($xPathSettings))
 					$feed->_attributes('xpath', $xPathSettings);
-				}
 			}
 
-			$feed->_attributes('path_entries_filter', Minz_Request::param('path_entries_filter', '', true));
+			$feed->_attributes('path_entries_filter', Minz_Request::paramString('path_entries_filter', true));
 
 			$values = array(
-				'name' => Minz_Request::param('name', ''),
+				'name' => Minz_Request::paramString('name'),
 				'kind' => $feed->kind(),
-				'description' => sanitizeHTML(Minz_Request::param('description', '', true)),
-				'website' => checkUrl(Minz_Request::param('website', '')),
-				'url' => checkUrl(Minz_Request::param('url', '')),
-				'category' => (int)Minz_Request::param('category', 0),
-				'pathEntries' => Minz_Request::param('path_entries', ''),
-				'priority' => (int)Minz_Request::param('priority', FreshRSS_Feed::PRIORITY_MAIN_STREAM),
+				'description' => sanitizeHTML(Minz_Request::paramString('description', true)),
+				'website' => checkUrl(Minz_Request::paramString('website')),
+				'url' => checkUrl(Minz_Request::paramString('url')),
+				'category' => Minz_Request::paramInt('category'),
+				'pathEntries' => Minz_Request::paramString('path_entries'),
+				'priority' => Minz_Request::paramInt('priority') ?: FreshRSS_Feed::PRIORITY_MAIN_STREAM,
 				'httpAuth' => $httpAuth,
 				'ttl' => $feed->ttl(true),
 				'attributes' => $feed->attributes(),
@@ -238,14 +247,14 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 
 			invalidateHttpCache();
 
-			$from = Minz_Request::param('from');
+			$from = Minz_Request::paramString('from');
 			switch ($from) {
 				case 'stats':
 					$url_redirect = array('c' => 'stats', 'a' => 'idle', 'params' => array('id' => $id, 'from' => 'stats'));
 					break;
 				case 'normal':
 				case 'reader':
-					$get = Minz_Request::param('get');
+					$get = Minz_Request::paramString('get');
 					if ($get) {
 						$url_redirect = array('c' => 'index', 'a' => $from, 'params' => array('get' => $get));
 					} else {
@@ -278,9 +287,9 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 
 		$categoryDAO = FreshRSS_Factory::createCategoryDao();
 
-		$id = Minz_Request::param('id');
+		$id = Minz_Request::paramInt('id');
 		$category = $categoryDAO->searchById($id);
-		if ($id === false || null === $category) {
+		if ($id === 0 || null === $category) {
 			Minz_Error::error(404);
 			return;
 		}
@@ -292,13 +301,13 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 			} else {
 				if (!Minz_Request::paramBoolean('enable_keep_max')) {
 					$keepMax = false;
-				} elseif (!$keepMax = Minz_Request::param('keep_max')) {
+				} elseif (($keepMax = Minz_Request::paramInt('keep_max')) !== 0) {
 					$keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
 				}
-				if ($enableRetentionPeriod = Minz_Request::paramBoolean('enable_keep_period')) {
+				if (Minz_Request::paramBoolean('enable_keep_period')) {
 					$keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD;
-					if (is_numeric(Minz_Request::param('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::param('keep_period_unit'))) {
-						$keepPeriod = str_replace('1', Minz_Request::param('keep_period_count'), Minz_Request::param('keep_period_unit'));
+					if (is_numeric(Minz_Request::paramString('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::paramString('keep_period_unit'))) {
+						$keepPeriod = str_replace('1', Minz_Request::paramString('keep_period_count'), Minz_Request::paramString('keep_period_unit'));
 					}
 				} else {
 					$keepPeriod = false;
@@ -306,17 +315,17 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 				$category->_attributes('archiving', [
 					'keep_period' => $keepPeriod,
 					'keep_max' => $keepMax,
-					'keep_min' => (int)Minz_Request::param('keep_min', 0),
+					'keep_min' => Minz_Request::paramInt('keep_min'),
 					'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'),
 					'keep_labels' => Minz_Request::paramBoolean('keep_labels'),
 					'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'),
 				]);
 			}
 
-			$position = Minz_Request::param('position');
-			$category->_attributes('position', '' === $position ? null : (int) $position);
+			$position = Minz_Request::paramInt('position') ?: null;
+			$category->_attributes('position', $position);
 
-			$opml_url = checkUrl(Minz_Request::param('opml_url', ''));
+			$opml_url = checkUrl(Minz_Request::paramString('opml_url'));
 			if ($opml_url != '') {
 				$category->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML);
 				$category->_attributes('opml_url', $opml_url);
@@ -327,7 +336,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 
 			$values = [
 				'kind' => $category->kind(),
-				'name' => Minz_Request::param('name', ''),
+				'name' => Minz_Request::paramString('name'),
 				'attributes' => $category->attributes(),
 			];
 

+ 14 - 17
app/Controllers/tagController.php

@@ -21,7 +21,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 			Minz_Error::error(403);
 		}
 		// If ajax request, we do not print layout
-		$this->ajax = Minz_Request::param('ajax');
+		$this->ajax = Minz_Request::paramBoolean('ajax');
 		if ($this->ajax) {
 			$this->view->_layout(false);
 			Minz_Request::_param('ajax');
@@ -33,16 +33,13 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 	 */
 	public function tagEntryAction(): void {
 		if (Minz_Request::isPost()) {
-			$id_tag = Minz_Request::param('id_tag');
-			$name_tag = Minz_Request::param('name_tag');
-			if (is_string($name_tag)) {
-				$name_tag = trim($name_tag);
-			}
-			$id_entry = Minz_Request::param('id_entry');
+			$id_tag = Minz_Request::paramInt('id_tag');
+			$name_tag = Minz_Request::paramString('name_tag');
+			$id_entry = Minz_Request::paramString('id_entry');
 			$checked = Minz_Request::paramTernary('checked');
-			if ($id_entry != false) {
+			if ($id_entry != '') {
 				$tagDAO = FreshRSS_Factory::createTagDao();
-				if ($id_tag == 0 && $name_tag != '' && $checked) {
+				if ($id_tag === 0 && $name_tag !== '' && $checked) {
 					if ($existing_tag = $tagDAO->searchByName($name_tag)) {
 						// Use existing tag
 						$tagDAO->tagEntry($existing_tag->id(), $id_entry, $checked);
@@ -51,7 +48,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 						$id_tag = $tagDAO->addTag(array('name' => $name_tag));
 					}
 				}
-				if ($id_tag != 0) {
+				if ($id_tag !== 0) {
 					$tagDAO->tagEntry($id_tag, $id_entry, $checked);
 				}
 			}
@@ -68,8 +65,8 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 
 	public function deleteAction(): void {
 		if (Minz_Request::isPost()) {
-			$id_tag = Minz_Request::param('id_tag');
-			if ($id_tag != false) {
+			$id_tag = Minz_Request::paramInt('id_tag');
+			if ($id_tag !== 0) {
 				$tagDAO = FreshRSS_Factory::createTagDao();
 				$tagDAO->deleteTag($id_tag);
 			}
@@ -88,7 +85,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 		$this->view->_layout(false);
 		header('Content-Type: application/json; charset=UTF-8');
 		header('Cache-Control: private, no-cache, no-store, must-revalidate');
-		$id_entry = Minz_Request::param('id_entry', 0);
+		$id_entry = Minz_Request::paramInt('id_entry');
 		$tagDAO = FreshRSS_Factory::createTagDao();
 		$this->view->tags = $tagDAO->getTagsForEntry($id_entry);
 	}
@@ -98,7 +95,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 			Minz_Error::error(405);
 		}
 
-		$name = Minz_Request::param('name');
+		$name = Minz_Request::paramString('name');
 		$lengthOfName = 0;
 		if (is_string($name)) {
 			$lengthOfName = strlen($name);
@@ -122,10 +119,10 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 			Minz_Error::error(405);
 		}
 
-		$targetName = Minz_Request::param('name');
-		$sourceId = Minz_Request::param('id_tag');
+		$targetName = Minz_Request::paramString('name');
+		$sourceId = Minz_Request::paramInt('id_tag');
 
-		if ($targetName == '' || $sourceId == '') {
+		if ($targetName == '' || $sourceId == 0) {
 			Minz_Error::error(400);
 			return;
 		}

+ 1 - 1
app/Controllers/updateController.php

@@ -222,7 +222,7 @@ class FreshRSS_update_Controller extends FreshRSS_ActionController {
 			Minz_Request::forward(array('c' => 'update'), true);
 		}
 
-		if (Minz_Request::param('post_conf', false)) {
+		if (Minz_Request::paramBoolean('post_conf')) {
 			if (self::isGit()) {
 				$res = !self::hasGitUpdate();
 			} else {

+ 37 - 37
app/Controllers/userController.php

@@ -58,14 +58,14 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 		}
 
 		if (Minz_Request::isPost()) {
-			$passwordPlain = Minz_Request::param('newPasswordPlain', '', true);
+			$passwordPlain = Minz_Request::paramString('newPasswordPlain', true);
 			Minz_Request::_param('newPasswordPlain');	//Discard plain-text password ASAP
 			$_POST['newPasswordPlain'] = '';
 
-			$username = Minz_Request::param('username');
-			$ok = self::updateUser($username, null, $passwordPlain, array(
-				'token' => Minz_Request::param('token', null),
-			));
+			$username = Minz_Request::paramString('username');
+			$ok = self::updateUser($username, null, $passwordPlain, [
+				'token' => Minz_Request::paramString('token') ?: null,
+			]);
 
 			if ($ok) {
 				$isSelfUpdate = Minz_User::name() === $username;
@@ -104,8 +104,8 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			$user_config = FreshRSS_Context::$user_conf;
 			$old_email = $user_config->mail_login;
 
-			$email = trim(Minz_Request::param('email', ''));
-			$passwordPlain = Minz_Request::param('newPasswordPlain', '', true);
+			$email = Minz_Request::paramString('email');
+			$passwordPlain = Minz_Request::paramString('newPasswordPlain', true);
 			Minz_Request::_param('newPasswordPlain');	//Discard plain-text password ASAP
 			$_POST['newPasswordPlain'] = '';
 
@@ -127,9 +127,9 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 				Minz_User::name(),
 				$email,
 				$passwordPlain,
-				array(
-					'token' => Minz_Request::param('token', null),
-				)
+				[
+					'token' => Minz_Request::paramString('token') ?: null,
+				]
 			);
 
 			Minz_Session::_param('passwordHash', FreshRSS_Context::$user_conf->passwordHash);
@@ -154,7 +154,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 		}
 
 		if (Minz_Request::isPost()) {
-			$username = Minz_Request::param('username');
+			$username = Minz_Request::paramString('username');
 
 			if (!FreshRSS_UserDAO::exists($username)) {
 				Minz_Error::error(404);
@@ -176,7 +176,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 		FreshRSS_View::prependTitle(_t('admin.user.title') . ' · ');
 
 		if (Minz_Request::isPost()) {
-			$action = Minz_Request::param('action');
+			$action = Minz_Request::paramString('action');
 			switch ($action) {
 				case 'delete':
 					$this->deleteAction();
@@ -203,7 +203,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 		}
 
 		$this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation;
-		$this->view->current_user = Minz_Request::param('u');
+		$this->view->current_user = Minz_Request::paramString('u');
 
 		foreach (listUsers() as $user) {
 			$this->view->users[$user] = $this->retrieveUserDetails($user);
@@ -289,12 +289,12 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 		if (Minz_Request::isPost()) {
 			$system_conf = FreshRSS_Context::$system_conf;
 
-			$new_user_name = Minz_Request::param('new_user_name');
-			$email = Minz_Request::param('new_user_email', '');
-			$passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true);
+			$new_user_name = Minz_Request::paramString('new_user_name');
+			$email = Minz_Request::paramString('new_user_email');
+			$passwordPlain = Minz_Request::paramString('new_user_passwordPlain', true);
 			$badRedirectUrl = [
-				'c' => Minz_Request::param('originController', 'auth'),
-				'a' => Minz_Request::param('originAction', 'register'),
+				'c' => Minz_Request::paramString('originController') ?: 'auth',
+				'a' => Minz_Request::paramString('originAction') ?: 'register',
 			];
 
 			if (!self::checkUsername($new_user_name)) {
@@ -319,7 +319,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			}
 
 			$tos_enabled = file_exists(TOS_FILENAME);
-			$accept_tos = Minz_Request::param('accept_tos', false);
+			$accept_tos = Minz_Request::paramBoolean('accept_tos');
 
 			if ($system_conf->force_email_validation && empty($email)) {
 				Minz_Request::bad(
@@ -342,12 +342,12 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 				);
 			}
 
-			$ok = self::createUser($new_user_name, $email, $passwordPlain, array(
-				'language' => Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language),
-				'timezone' => Minz_Request::param('new_user_timezone', ''),
+			$ok = self::createUser($new_user_name, $email, $passwordPlain, [
+				'language' => Minz_Request::paramString('new_user_language') ?: FreshRSS_Context::$user_conf->language,
+				'timezone' => Minz_Request::paramString('new_user_timezone'),
 				'is_admin' => Minz_Request::paramBoolean('new_user_is_admin'),
 				'enabled' => true,
-			));
+			]);
 			Minz_Request::_param('new_user_passwordPlain');	//Discard plain-text password ASAP
 			$_POST['new_user_passwordPlain'] = '';
 			invalidateHttpCache();
@@ -373,9 +373,9 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			}
 		}
 
-		$redirect_url = urldecode(Minz_Request::param('r', false, true));
-		if (!$redirect_url) {
-			$redirect_url = array('c' => 'user', 'a' => 'manage');
+		$redirect_url = urldecode(Minz_Request::paramString('r', true));
+		if ($redirect_url === '') {
+			$redirect_url = ['c' => 'user', 'a' => 'manage'];
 		}
 		Minz_Request::forward($redirect_url, true);
 	}
@@ -423,10 +423,10 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 		FreshRSS_View::prependTitle(_t('user.email.validation.title') . ' · ');
 		$this->view->_layout('simple');
 
-		$username = Minz_Request::param('username');
-		$token = Minz_Request::param('token');
+		$username = Minz_Request::paramString('username');
+		$token = Minz_Request::paramString('token');
 
-		if ($username) {
+		if ($username !== '') {
 			$user_config = get_user_configuration($username);
 		} elseif (FreshRSS_Auth::hasAccess()) {
 			$user_config = FreshRSS_Context::$user_conf;
@@ -447,7 +447,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			);
 		}
 
-		if ($token) {
+		if ($token != '') {
 			if ($user_config->email_validation_token !== $token) {
 				Minz_Request::bad(
 					_t('user.email.validation.feedback.wrong_token'),
@@ -525,16 +525,16 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 	 * @todo clean up this method. Idea: create a User->clean() method.
 	 */
 	public function deleteAction() {
-		$username = Minz_Request::param('username');
+		$username = Minz_Request::paramString('username');
 		$self_deletion = Minz_User::name() === $username;
 
 		if (!FreshRSS_Auth::hasAccess('admin') && !$self_deletion) {
 			Minz_Error::error(403);
 		}
 
-		$redirect_url = urldecode(Minz_Request::param('r', false, true));
-		if (!$redirect_url) {
-			$redirect_url = array('c' => 'user', 'a' => 'manage');
+		$redirect_url = urldecode(Minz_Request::paramString('r', true));
+		if ($redirect_url === '') {
+			$redirect_url = ['c' => 'user', 'a' => 'manage'];
 		}
 
 		if (Minz_Request::isPost()) {
@@ -542,7 +542,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			if ($self_deletion) {
 				// We check the password if it’s a self-destruction
 				$nonce = Minz_Session::param('nonce', '');
-				$challenge = Minz_Request::param('challenge', '');
+				$challenge = Minz_Request::paramString('challenge');
 
 				$ok &= FreshRSS_FormAuth::checkCredentials(
 					$username, FreshRSS_Context::$user_conf->passwordHash,
@@ -593,7 +593,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			Minz_Error::error(403);
 		}
 
-		$username = Minz_Request::param('username');
+		$username = Minz_Request::paramString('username');
 		if (!FreshRSS_UserDAO::exists($username)) {
 			Minz_Error::error(404);
 		}
@@ -620,7 +620,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController {
 			Minz_Error::error(403);
 		}
 
-		$username = Minz_Request::param('username');
+		$username = Minz_Request::paramString('username');
 		if (!FreshRSS_UserDAO::exists($username)) {
 			Minz_Error::error(404);
 		}

+ 1 - 1
app/FreshRSS.php

@@ -155,7 +155,7 @@ class FreshRSS extends Minz_FrontController {
 
 	private static function loadNotifications(): void {
 		$notif = Minz_Request::getNotification();
-		if ($notif) {
+		if (!empty($notif)) {
 			FreshRSS_View::_param('notification', $notif);
 		}
 	}

+ 2 - 2
app/Models/Auth.php

@@ -169,9 +169,9 @@ class FreshRSS_Auth {
 		]);
 
 		$username = '';
-		$token_param = Minz_Request::param('token', '');
+		$token_param = Minz_Request::paramString('token');
 		if ($token_param != '') {
-			$username = trim(Minz_Request::param('user', ''));
+			$username = Minz_Request::paramString('user');
 			if ($username != '') {
 				$conf = get_user_configuration($username);
 				if ($conf == null) {

+ 8 - 12
app/Models/Context.php

@@ -207,12 +207,10 @@ final class FreshRSS_Context {
 			self::$categories, 1
 		);
 
-		self::_get(Minz_Request::param('get', 'a', false));
+		self::_get(Minz_Request::paramString('get') ?: 'a');
 
-		self::$state = Minz_Request::param(
-			'state', self::$user_conf->default_state
-		);
-		$state_forced_by_user = Minz_Request::param('state') !== false;
+		self::$state = Minz_Request::paramInt('state') ?: self::$user_conf->default_state;
+		$state_forced_by_user = Minz_Request::paramString('state') !== '';
 		if (!$state_forced_by_user && !self::isStateEnabled(FreshRSS_Entry::STATE_READ)) {
 			if (self::$user_conf->default_view === 'adaptive' && self::$get_unread <= 0) {
 				self::$state |= FreshRSS_Entry::STATE_READ;
@@ -223,18 +221,16 @@ final class FreshRSS_Context {
 			}
 		}
 
-		self::$search = new FreshRSS_BooleanSearch(Minz_Request::param('search', ''));
-		self::$order = Minz_Request::param(
-			'order', self::$user_conf->sort_order
-		);
-		self::$number = (int)Minz_Request::param('nb', self::$user_conf->posts_per_page);
+		self::$search = new FreshRSS_BooleanSearch(Minz_Request::paramString('search'));
+		self::$order = Minz_Request::paramString('order') ?: self::$user_conf->sort_order;
+		self::$number = Minz_Request::paramInt('nb') ?: self::$user_conf->posts_per_page;
 		if (self::$number > self::$user_conf->max_posts_per_rss) {
 			self::$number = max(
 				self::$user_conf->max_posts_per_rss,
 				self::$user_conf->posts_per_page);
 		}
-		self::$first_id = Minz_Request::param('next', '');
-		self::$sinceHours = (int)Minz_Request::param('hours', 0);
+		self::$first_id = Minz_Request::paramString('next');
+		self::$sinceHours = Minz_Request::paramInt('hours');
 	}
 
 	/**

+ 1 - 1
app/Models/SystemConfiguration.php

@@ -22,7 +22,7 @@
  * @property-read bool $pubsubhubbub_enabled
  * @property-read string $salt
  * @property-read bool $simplepie_syslog_enabled
- * @property string $unsafe_autologin_enabled
+ * @property bool $unsafe_autologin_enabled
  * @property-read array<string> $trusted_sources
  */
 final class FreshRSS_SystemConfiguration extends Minz_Configuration {

+ 2 - 2
app/Models/TagDAO.php

@@ -324,7 +324,7 @@ SQL;
 		}
 	}
 
-	public function tagEntry($id_tag, $id_entry, $checked = true) {
+	public function tagEntry(int $id_tag, string $id_entry, bool $checked = true) {
 		if ($checked) {
 			$sql = 'INSERT ' . $this->sqlIgnore() . ' INTO `_entrytag`(id_tag, id_entry) VALUES(?, ?)';
 		} else {
@@ -342,7 +342,7 @@ SQL;
 		}
 	}
 
-	public function getTagsForEntry($id_entry) {
+	public function getTagsForEntry(int $id_entry) {
 		$sql = 'SELECT t.id, t.name, et.id_entry IS NOT NULL as checked '
 			 . 'FROM `_tag` t '
 			 . 'LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id AND et.id_entry=? '

+ 2 - 2
app/Models/UserConfiguration.php

@@ -12,7 +12,7 @@
  * @property bool $bottomline_sharing
  * @property bool $bottomline_tags
  * @property string $content_width
- * @property-read string $default_state
+ * @property-read int $default_state
  * @property string $default_view
  * @property string|bool $display_categories
  * @property string $show_tags
@@ -47,7 +47,7 @@
  * @property bool $icons_as_emojis
  * @property int $simplify_over_n_feeds
  * @property bool $show_nav_buttons
- * @property string $sort_order
+ * @property 'ASC'|'DESC' $sort_order
  * @property array<string,array<string>> $sharing
  * @property array<string,string> $shortcuts
  * @property bool $sides_close_article

+ 1 - 1
app/Services/ExportService.php

@@ -99,7 +99,7 @@ class FreshRSS_Export_Service {
 	 * @return array|null First item is the filename, second item is the content.
 	 *                    It also can return null if the feed doesn’t exist.
 	 */
-	public function generateFeedEntries($feed_id, $max_number_entries) {
+	public function generateFeedEntries(int $feed_id, int $max_number_entries) {
 		$feed = $this->feed_dao->searchById($feed_id);
 		if (!$feed) {
 			return null;

+ 4 - 4
app/layout/aside_feed.phtml

@@ -8,8 +8,8 @@
 		$class = ' state_unread';
 	}
 
-	$state_filter_manual = Minz_Request::param('state', '');
-	if ($state_filter_manual != '') {
+	$state_filter_manual = Minz_Request::paramString('state');
+	if ($state_filter_manual !== '') {
 		$state_filter_manual = '&state=' . $state_filter_manual;
 	}
 ?>
@@ -164,8 +164,8 @@
 		<?php } ?>
 		<li class="item link website"><a target="_blank" rel="noreferrer" href="http://example.net/"><?= _t('gen.action.see_website') ?></a></li>
 		<?php if (FreshRSS_Auth::hasAccess()) {
-		$get = Minz_Request::param('get');
-		if (!$get) {
+		$get = Minz_Request::paramString('get');
+		if ($get === '') {
 			$url = _url('subscription', 'feed', 'id', '------', 'from', $actual_view);
 		} else {
 			$url = _url('subscription', 'feed', 'id', '------', 'get', $get, 'from', $actual_view);

+ 6 - 6
app/layout/header.phtml

@@ -24,18 +24,18 @@
 				<input type="hidden" name="a" value="<?= $param_a ?>" />
 				<?php } ?>
 
-				<?php $get = Minz_Request::param('get', ''); ?>
-				<?php if ($get != '') { ?>
+				<?php $get = Minz_Request::paramString('get'); ?>
+				<?php if ($get !== '') { ?>
 				<input type="hidden" name="get" value="<?= $get ?>" />
 				<?php } ?>
 
-				<?php $order = Minz_Request::param('order', ''); ?>
-				<?php if ($order != '') { ?>
+				<?php $order = Minz_Request::paramString('order'); ?>
+				<?php if ($order !== '') { ?>
 				<input type="hidden" name="order" value="<?= $order ?>" />
 				<?php } ?>
 
-				<?php $state = Minz_Request::param('state', ''); ?>
-				<?php if ($state != '') { ?>
+				<?php $state = Minz_Request::paramString('state'); ?>
+				<?php if ($state !== '') { ?>
 				<input type="hidden" name="state" value="<?= $state ?>" />
 				<?php } ?>
 

+ 1 - 1
app/layout/layout.phtml

@@ -55,7 +55,7 @@ if (_t('gen.dir') === 'rtl') {
 	</head>
 	<body class="<?= Minz_Request::actionName() ?><?= (FreshRSS_Context::$user_conf->darkMode === 'no') ? '' : ' darkMode_' . FreshRSS_Context::$user_conf->darkMode ?>">
 <?php
-	if (!Minz_Request::param('ajax')) {
+	if (!Minz_Request::paramBoolean('ajax')) {
 		flush();
 		$this->partial('header');
 	}

+ 6 - 6
app/layout/nav_menu.phtml

@@ -44,18 +44,18 @@
 							<input type="hidden" name="a" value="<?= $param_a ?>" />
 							<?php } ?>
 
-							<?php $get = Minz_Request::param('get', ''); ?>
-							<?php if ($get != '') { ?>
+							<?php $get = Minz_Request::paramString('get'); ?>
+							<?php if ($get !== '') { ?>
 							<input type="hidden" name="get" value="<?= $get ?>" />
 							<?php } ?>
 
-							<?php $order = Minz_Request::param('order', ''); ?>
-							<?php if ($order != '') { ?>
+							<?php $order = Minz_Request::paramString('order'); ?>
+							<?php if ($order !== '') { ?>
 							<input type="hidden" name="order" value="<?= $order ?>" />
 							<?php } ?>
 
-							<?php $state = Minz_Request::param('state', ''); ?>
-							<?php if ($state != '') { ?>
+							<?php $state = Minz_Request::paramString('state'); ?>
+							<?php if ($state !== '') { ?>
 							<input type="hidden" name="state" value="<?= $state ?>" />
 							<?php } ?>
 

+ 1 - 1
app/views/entry/bookmark.phtml

@@ -8,7 +8,7 @@ $url = array(
 	'params' => $_GET,
 );
 
-$url['params']['is_favorite'] = Minz_Request::param('is_favorite', true) ? '0' : '1';
+$url['params']['is_favorite'] = (Minz_Request::paramTernary('is_favorite') ?? true) ? '0' : '1';
 
 FreshRSS::loadStylesAndScripts();
 echo json_encode(array(

+ 5 - 5
app/views/helpers/feed/update.phtml

@@ -17,12 +17,12 @@
 	<?php } ?>
 
 	<?php
-	$from = Minz_Request::param('from');
-	if ($from === false) {
+	$from = Minz_Request::paramString('from');
+	if ($from === '') {
 		$url = _url('subscription', 'feed', 'id', $this->feed->id(), '#', 'slider');
 	} else {
-		$get = Minz_Request::param('get');
-		if (!$get) {
+		$get = Minz_Request::paramString('get');
+		if ($get === '') {
 			$url = _url('subscription', 'feed', 'id', $this->feed->id(), 'from', $from, '#', 'slider');
 		} else {
 			$url = _url('subscription', 'feed', 'id', $this->feed->id(), 'from', $from, 'get', $get, '#', 'slider');
@@ -132,7 +132,7 @@
 				<button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button>
 				<button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button>
 				<?php
-				if ($from === false) {
+				if ($from === '') {
 					$url = _url('feed', 'delete', 'id', $this->feed->id());
 				} else {
 					$url = _url('feed', 'delete', 'id', $this->feed->id(), 'from', $from);

+ 1 - 1
app/views/helpers/javascript_vars.phtml

@@ -8,7 +8,7 @@ echo htmlspecialchars(json_encode(array(
 		'anonymous' => !FreshRSS_Auth::hasAccess(),
 		'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(),
 		'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'),
-		'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order),
+		'display_order' => Minz_Request::paramString('order') ?: FreshRSS_Context::$user_conf->sort_order,
 		'display_categories' => FreshRSS_Context::$user_conf->display_categories,
 		'auto_mark_article' => !!$mark['article'],
 		'auto_mark_site' => !!$mark['site'],

+ 1 - 1
app/views/index/normal.phtml

@@ -1,6 +1,6 @@
 <?php
 /** @var FreshRSS_View $this */
-if (!Minz_Request::param('ajax')) {
+if (!Minz_Request::paramBoolean('ajax')) {
 	$this->partial('aside_feed');
 	$this->partial('nav_menu');
 }

+ 1 - 1
app/views/index/reader.phtml

@@ -1,6 +1,6 @@
 <?php
 /** @var FreshRSS_View $this */
-if (!Minz_Request::param('ajax')) {
+if (!Minz_Request::paramBoolean('ajax')) {
 	$this->partial('aside_feed');
 	$this->partial('nav_menu');
 }

+ 1 - 1
app/views/subscription/add.phtml

@@ -51,7 +51,7 @@
 							continue;
 						}
 				?>
-				<option value="<?= $cat->id() ?>"<?= $cat->id() == ( Minz_Request::param('cat_id') ?: 1 ) ? ' selected="selected"' : '' ?>>
+				<option value="<?= $cat->id() ?>"<?= $cat->id() === (Minz_Request::paramInt('cat_id') ?: 1) ? ' selected="selected"' : '' ?>>
 					<?= $cat->name() ?>
 				</option>
 				<?php } ?>

+ 1 - 1
app/views/subscription/feed.phtml

@@ -1,7 +1,7 @@
 <?php /** @var FreshRSS_View $this */ ?>
 <?php
 
-if (!Minz_Request::param('ajax')) {
+if (!Minz_Request::paramBoolean('ajax')) {
 	$this->partial('aside_subscription');
 }
 

+ 2 - 2
docs/en/developers/Minz/index.md

@@ -67,7 +67,7 @@ Code example:
 <?php
 
 $default_value = 'foo';
-$param = Minz_Request::param('bar', $default_value);
+$param = Minz_Request::paramString('bar') ?: $default_value;
 
 // Display the value of the parameter `bar` (passed via GET or POST)
 // or "foo" if the parameter does not exist.
@@ -78,7 +78,7 @@ Minz_Request::_param('bar', 'baz');
 
 // Will necessarily display "baz" since we have just forced its value.
 // Note that the second parameter (default) is optional.
-echo Minz_Request::param('bar');
+echo Minz_Request::paramString('bar');
 
 ?>
 ```

+ 2 - 2
docs/fr/developers/Minz/index.md

@@ -97,7 +97,7 @@ l’aide de la classe `Minz_Request`. Exemple de code :
 <?php
 
 $default_value = 'foo';
-$param = Minz_Request::param('bar', $default_value);
+$param = Minz_Request::paramString('bar') ?: $default_value;
 
 // Affichera la valeur du paramètre `bar` (passé via GET ou POST)
 // ou "foo" si le paramètre n’existe pas.
@@ -108,7 +108,7 @@ Minz_Request::_param('bar', 'baz');
 
 // Affichera forcément "baz" puisque nous venons de forcer sa valeur.
 // Notez que le second paramètre (valeur par défaut) est facultatif.
-echo Minz_Request::param('bar');
+echo Minz_Request::paramString('bar');
 
 ?>
 ```

+ 1 - 1
lib/Minz/Helper.php

@@ -13,7 +13,7 @@ class Minz_Helper {
 	 * Wrapper for htmlspecialchars.
 	 * Force UTf-8 value and can be used on array too.
 	 * @param string|array<string> $var
-	 * @return string|array<string>
+	 * @return ($var is array ? array<string> : string)
 	 */
 	public static function htmlspecialchars_utf8($var) {
 		if (is_array($var)) {

+ 80 - 51
lib/Minz/Request.php

@@ -8,25 +8,32 @@
  * Request représente la requête http
  */
 class Minz_Request {
+	/** @var string */
 	private static $controller_name = '';
+	/** @var string */
 	private static $action_name = '';
+	/** @var array<string,mixed> */
 	private static $params = array();
 
+	/** @var string */
 	private static $default_controller_name = 'index';
+	/** @var string */
 	private static $default_action_name = 'index';
 
-	private static $originalRequest;
+	/** @var array{'c':string,'a':string,'params':array<string,mixed>}|null */
+	private static $originalRequest = null;
 
 	/**
 	 * Getteurs
 	 */
-	public static function controllerName() {
+	public static function controllerName(): string {
 		return self::$controller_name;
 	}
-	public static function actionName() {
+	public static function actionName(): string {
 		return self::$action_name;
 	}
-	public static function params() {
+	/** @return array<string,mixed> */
+	public static function params(): array {
 		return self::$params;
 	}
 	/**
@@ -35,6 +42,7 @@ class Minz_Request {
 	 * @param mixed $default default value, if no parameter is given
 	 * @param bool $specialchars special characters
 	 * @return mixed value of the parameter
+	 * @deprecated use typed versions instead
 	 */
 	public static function param(string $key, $default = false, bool $specialchars = false) {
 		if (isset(self::$params[$key])) {
@@ -49,6 +57,15 @@ class Minz_Request {
 		}
 	}
 
+	/** @return array<string|int,string|array<string,string>> */
+	public static function paramArray(string $key, bool $specialchars = false): array {
+		if (empty(self::$params[$key]) || !is_array(self::$params[$key])) {
+			return [];
+		}
+
+		return $specialchars ? Minz_Helper::htmlspecialchars_utf8(self::$params[$key]) : self::$params[$key];
+	}
+
 	/** @return bool|null */
 	public static function paramTernary(string $key) {
 		if (isset(self::$params[$key])) {
@@ -71,11 +88,19 @@ class Minz_Request {
 		return $value;
 	}
 
-	public static function paramString(string $key): string {
+	public static function paramInt(string $key): int {
+		if (!empty(self::$params[$key])) {
+			return intval(self::$params[$key]);
+		}
+		return 0;
+	}
+
+	public static function paramString(string $key, bool $specialchars = false): string {
 		if (isset(self::$params[$key])) {
 			$s = self::$params[$key];
 			if (is_string($s)) {
-				return trim($s);
+				$s = trim($s);
+				return $specialchars ? $s : htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
 			}
 			if (is_int($s) || is_bool($s)) {
 				return (string)$s;
@@ -90,30 +115,41 @@ class Minz_Request {
 	 * It will return an array where each cell contains one line of a text. The new line
 	 * character is used to break the text into lines. This method is well suited to use
 	 * to split textarea content.
+	 * @param array<string> $default
+	 * @return array<string>
 	 */
-	public static function paramTextToArray($key, $default = []) {
+	public static function paramTextToArray(string $key, array $default = []): array {
 		if (isset(self::$params[$key])) {
 			return preg_split('/\R/', self::$params[$key]);
 		}
 		return $default;
 	}
-	public static function defaultControllerName() {
+
+	public static function defaultControllerName(): string {
 		return self::$default_controller_name;
 	}
-	public static function defaultActionName() {
+	public static function defaultActionName(): string {
 		return self::$default_action_name;
 	}
-	public static function currentRequest() {
-		return array(
+	/** @return array{'c':string,'a':string,'params':array<string,mixed>} */
+	public static function currentRequest(): array {
+		return [
 			'c' => self::$controller_name,
 			'a' => self::$action_name,
 			'params' => self::$params,
-		);
+		];
 	}
-	public static function originalRequest() {
+
+	/** @return array{'c':string,'a':string,'params':array<string,mixed>}|null */
+	public static function originalRequest(): ?array {
 		return self::$originalRequest;
 	}
-	public static function modifiedCurrentRequest(array $extraParams = null) {
+
+	/**
+	 * @param array<string,mixed>|null $extraParams
+	 * @return array{'c':string,'a':string,'params':array<string,mixed>}
+	 */
+	public static function modifiedCurrentRequest(?array $extraParams = null): array {
 		unset(self::$params['ajax']);
 		$currentRequest = self::currentRequest();
 		if (null !== $extraParams) {
@@ -125,20 +161,21 @@ class Minz_Request {
 	/**
 	 * Setteurs
 	 */
-	public static function _controllerName($controller_name) {
+	public static function _controllerName(string $controller_name): void {
 		self::$controller_name = $controller_name;
 	}
-	public static function _actionName($action_name) {
+
+	public static function _actionName(string $action_name): void {
 		self::$action_name = $action_name;
 	}
-	public static function _params($params) {
-		if (!is_array($params)) {
-			$params = array($params);
-		}
 
+	/** @param array<string,string> $params */
+	public static function _params(array $params): void {
 		self::$params = $params;
 	}
-	public static function _param($key, $value = false) {
+
+	/** @param array|mixed $value */
+	public static function _param(string $key, $value = false): void {
 		if ($value === false) {
 			unset(self::$params[$key]);
 		} else {
@@ -149,15 +186,13 @@ class Minz_Request {
 	/**
 	 * Initialise la Request
 	 */
-	public static function init() {
+	public static function init(): void {
 		self::initJSON();
 	}
 
-	public static function is($controller_name, $action_name) {
-		return (
-			self::$controller_name === $controller_name &&
-			self::$action_name === $action_name
-		);
+	public static function is(string $controller_name, string $action_name): bool {
+		return self::$controller_name === $controller_name &&
+			self::$action_name === $action_name;
 	}
 
 	/**
@@ -193,10 +228,7 @@ class Minz_Request {
 		return 'http';
 	}
 
-	/**
-	 * @return string
-	 */
-	private static function extractHost() {
+	private static function extractHost(): string {
 		if ('' != $host = ($_SERVER['HTTP_X_FORWARDED_HOST'] ?? '')) {
 			return parse_url("http://{$host}", PHP_URL_HOST);
 		}
@@ -210,10 +242,7 @@ class Minz_Request {
 		return 'localhost';
 	}
 
-	/**
-	 * @return integer
-	 */
-	private static function extractPort() {
+	private static function extractPort(): int {
 		if ('' != $port = ($_SERVER['HTTP_X_FORWARDED_PORT'] ?? '')) {
 			return intval($port);
 		}
@@ -271,7 +300,7 @@ class Minz_Request {
 	 * @return boolean true if server is accessible, false otherwise.
 	 * @todo improve test with a more valid technique (e.g. test with an external server?)
 	 */
-	public static function serverIsPublic($address) {
+	public static function serverIsPublic(string $address): bool {
 		if (strlen($address) < strlen('http://a.bc')) {
 			return false;
 		}
@@ -297,14 +326,14 @@ class Minz_Request {
 		return (bool)$is_public;
 	}
 
-	private static function requestId() {
+	private static function requestId(): string {
 		if (empty($_GET['rid']) || !ctype_xdigit($_GET['rid'])) {
 			$_GET['rid'] = uniqid();
 		}
 		return $_GET['rid'];
 	}
 
-	private static function setNotification($type, $content) {
+	private static function setNotification(string $type, string $content): void {
 		Minz_Session::lock();
 		$requests = Minz_Session::param('requests', []);
 		$requests[self::requestId()] = [
@@ -315,15 +344,16 @@ class Minz_Request {
 		Minz_Session::unlock();
 	}
 
-	public static function setGoodNotification($content) {
+	public static function setGoodNotification(string $content): void {
 		self::setNotification('good', $content);
 	}
 
-	public static function setBadNotification($content) {
+	public static function setBadNotification(string $content): void {
 		self::setNotification('bad', $content);
 	}
 
-	public static function getNotification() {
+	/** @return array<string,string>|null */
+	public static function getNotification(): ?array {
 		$notif = null;
 		Minz_Session::lock();
 		$requests = Minz_Session::param('requests');
@@ -344,12 +374,12 @@ class Minz_Request {
 
 	/**
 	 * Relance une requête
-	 * @param array<string,string|array<string,string>> $url l'url vers laquelle est relancée la requête
+	 * @param array{'c'?:string,'a'?:string,'params'?:array<string,mixed>} $url l'url vers laquelle est relancée la requête
 	 * @param bool $redirect si vrai, force la redirection http
 	 *                > sinon, le dispatcher recharge en interne
 	 */
-	public static function forward($url = array(), $redirect = false) {
-		if (Minz_Request::originalRequest() === null && strpos('auth', json_encode($url)) !== false) {
+	public static function forward(array $url = [], bool $redirect = false): void {
+		if (empty(Minz_Request::originalRequest()) && strpos('auth', json_encode($url)) !== false) {
 			self::$originalRequest = $url;
 		}
 
@@ -358,7 +388,7 @@ class Minz_Request {
 			exit();
 		}
 
-		$url = Minz_Url::checkUrl($url);
+		$url = Minz_Url::checkControllerUrl($url);
 		$url['params']['rid'] = self::requestId();
 
 		if ($redirect) {
@@ -375,13 +405,12 @@ class Minz_Request {
 		}
 	}
 
-
 	/**
 	 * Wrappers good notifications + redirection
 	 * @param string $msg notification content
-	 * @param array<string,string|array<string,string>> $url url array to where we should be forwarded
+	 * @param array{'c'?:string,'a'?:string,'params'?:array<string,mixed>} $url url array to where we should be forwarded
 	 */
-	public static function good($msg, $url = array()) {
+	public static function good(string $msg, array $url = []): void {
 		Minz_Request::setGoodNotification($msg);
 		Minz_Request::forward($url, true);
 	}
@@ -389,9 +418,9 @@ class Minz_Request {
 	/**
 	 * Wrappers bad notifications + redirection
 	 * @param string $msg notification content
-	 * @param array<string,string|array<string,mixed>> $url url array to where we should be forwarded
+	 * @param array{'c'?:string,'a'?:string,'params'?:array<string,mixed>} $url url array to where we should be forwarded
 	 */
-	public static function bad($msg, $url = array()) {
+	public static function bad(string $msg, array $url = []): void {
 		Minz_Request::setBadNotification($msg);
 		Minz_Request::forward($url, true);
 	}
@@ -399,7 +428,7 @@ class Minz_Request {
 	/**
 	 * Allows receiving POST data as application/json
 	 */
-	private static function initJSON() {
+	private static function initJSON(): void {
 		if ('application/json' !== self::extractContentType()) {
 			return;
 		}

+ 13 - 15
lib/Minz/Url.php

@@ -19,7 +19,7 @@ class Minz_Url {
 		$isArray = is_array($url);
 
 		if ($isArray) {
-			$url = self::checkUrl($url);
+			$url = self::checkControllerUrl($url);
 		}
 
 		$url_string = '';
@@ -107,23 +107,21 @@ class Minz_Url {
 	}
 
 	/**
-	 * Vérifie que les éléments du tableau représentant une url soit ok
-	 * @param array<string,array<string,string>> $url sous forme de tableau
-	 * @return array<string,array<string,string>> url vérifié
+	 * Check that all array elements representing the controller URL are OK
+	 * @param array<string,array<string,string>> $url controller URL as array
+	 * @return array{'c':string,'a':string,'params':array<string,mixed>} Verified controller URL as array
 	 */
-	public static function checkUrl ($url) {
+	public static function checkControllerUrl(array $url) {
 		$url_checked = $url;
 
-		if (is_array ($url)) {
-			if (!isset ($url['c'])) {
-				$url_checked['c'] = Minz_Request::defaultControllerName ();
-			}
-			if (!isset ($url['a'])) {
-				$url_checked['a'] = Minz_Request::defaultActionName ();
-			}
-			if (!isset ($url['params'])) {
-				$url_checked['params'] = array ();
-			}
+		if (empty($url['c'])) {
+			$url_checked['c'] = Minz_Request::defaultControllerName();
+		}
+		if (empty($url['a'])) {
+			$url_checked['a'] = Minz_Request::defaultActionName();
+		}
+		if (empty($url['params'])) {
+			$url_checked['params'] = [];
 		}
 
 		return $url_checked;

+ 0 - 1
tests/phpstan-next.txt

@@ -30,7 +30,6 @@
 ./lib/Minz/Log.php
 ./lib/Minz/Migrator.php
 ./lib/Minz/Paginator.php
-./lib/Minz/Request.php
 ./lib/Minz/Session.php
 ./lib/Minz/Translate.php
 ./lib/Minz/View.php