ソースを参照

Add PHPStan (#4021)

* Add PHPStan
#fix https://github.com/FreshRSS/FreshRSS/issues/4016
https://phpstan.org/

```sh
composer run-script phpstan
```

* More fixes

* Fix global variables

* Add .phtml

* Fix merge
https://github.com/FreshRSS/FreshRSS/pull/4090

* Fix more warnings

* Fix view errors and enable in CI

* ReturnTypeWillChange

* Dynamic view type

* Fix Minz static/self bug
Alexandre Alapetite 4 年 前
コミット
77e9877316
100 ファイル変更629 行追加272 行削除
  1. 3 0
      .github/workflows/tests.yml
  2. 4 4
      app/Controllers/authController.php
  3. 11 10
      app/Controllers/configureController.php
  4. 8 1
      app/Controllers/entryController.php
  5. 1 1
      app/Controllers/errorController.php
  6. 1 1
      app/Controllers/extensionController.php
  7. 9 8
      app/Controllers/feedController.php
  8. 8 1
      app/Controllers/importExportController.php
  9. 10 10
      app/Controllers/indexController.php
  10. 7 3
      app/Controllers/statsController.php
  11. 5 5
      app/Controllers/subscriptionController.php
  12. 10 3
      app/Controllers/tagController.php
  13. 6 2
      app/Controllers/updateController.php
  14. 7 6
      app/Controllers/userController.php
  15. 7 5
      app/FreshRSS.php
  16. 4 2
      app/Models/DatabaseDAO.php
  17. 1 1
      app/Models/EntryDAO.php
  18. 0 1
      app/Models/Search.php
  19. 1 1
      app/Models/TagDAO.php
  20. 3 2
      app/Models/UserDAO.php
  21. 119 0
      app/Models/View.php
  22. 7 7
      app/SQL/install.sql.mysql.php
  23. 6 6
      app/SQL/install.sql.pgsql.php
  24. 6 6
      app/SQL/install.sql.sqlite.php
  25. 3 3
      app/Services/ExportService.php
  26. 14 14
      app/actualize_script.php
  27. 1 0
      app/layout/aside_feed.phtml
  28. 7 4
      app/layout/layout.phtml
  29. 7 4
      app/layout/simple.phtml
  30. 1 0
      app/views/auth/formLogin.phtml
  31. 4 1
      app/views/auth/index.phtml
  32. 1 0
      app/views/auth/register.phtml
  33. 5 2
      app/views/configure/archiving.phtml
  34. 5 2
      app/views/configure/display.phtml
  35. 5 2
      app/views/configure/integration.phtml
  36. 4 1
      app/views/configure/queries.phtml
  37. 1 0
      app/views/configure/query.phtml
  38. 6 3
      app/views/configure/reading.phtml
  39. 5 2
      app/views/configure/shortcut.phtml
  40. 4 1
      app/views/configure/system.phtml
  41. 1 0
      app/views/entry/bookmark.phtml
  42. 1 0
      app/views/entry/read.phtml
  43. 3 2
      app/views/error/index.phtml
  44. 1 1
      app/views/extension/configure.phtml
  45. 4 1
      app/views/extension/index.phtml
  46. 1 0
      app/views/feed/add.phtml
  47. 5 2
      app/views/feed/contentSelectorPreview.phtml
  48. 2 1
      app/views/helpers/category/update.phtml
  49. 1 0
      app/views/helpers/configure/query.phtml
  50. 1 0
      app/views/helpers/export/articles.phtml
  51. 1 0
      app/views/helpers/export/opml.phtml
  52. 1 0
      app/views/helpers/extension/configure.phtml
  53. 1 0
      app/views/helpers/extension/details.phtml
  54. 1 0
      app/views/helpers/feed/update.phtml
  55. 1 0
      app/views/helpers/index/normal/entry_bottom.phtml
  56. 1 0
      app/views/helpers/index/normal/entry_header.phtml
  57. 1 0
      app/views/helpers/javascript_vars.phtml
  58. 5 1
      app/views/helpers/logs_pagination.phtml
  59. 1 0
      app/views/helpers/pagination.phtml
  60. 1 0
      app/views/importExport/export.phtml
  61. 4 1
      app/views/importExport/index.phtml
  62. 6 3
      app/views/index/about.phtml
  63. 1 0
      app/views/index/global.phtml
  64. 1 0
      app/views/index/logs.phtml
  65. 1 0
      app/views/index/normal.phtml
  66. 1 0
      app/views/index/reader.phtml
  67. 1 0
      app/views/index/rss.phtml
  68. 1 0
      app/views/index/tos.phtml
  69. 1 0
      app/views/javascript/actualize.phtml
  70. 1 0
      app/views/javascript/nbUnreadsPerFeed.phtml
  71. 1 0
      app/views/javascript/nonce.phtml
  72. 4 1
      app/views/stats/idle.phtml
  73. 4 1
      app/views/stats/index.phtml
  74. 4 1
      app/views/stats/repartition.phtml
  75. 5 2
      app/views/subscription/add.phtml
  76. 4 1
      app/views/subscription/bookmarklet.phtml
  77. 1 1
      app/views/subscription/category.phtml
  78. 1 0
      app/views/subscription/feed.phtml
  79. 5 2
      app/views/subscription/index.phtml
  80. 1 0
      app/views/tag/getTagsForEntry.phtml
  81. 5 2
      app/views/tag/index.phtml
  82. 8 2
      app/views/update/apply.phtml
  83. 4 1
      app/views/update/checkInstall.phtml
  84. 4 1
      app/views/update/index.phtml
  85. 4 1
      app/views/user/details.phtml
  86. 5 2
      app/views/user/manage.phtml
  87. 2 1
      app/views/user/profile.phtml
  88. 1 0
      app/views/user/validateEmail.phtml
  89. 1 0
      app/views/user_mailer/email_need_validation.txt.php
  90. 4 2
      cli/_update-or-create-user.php
  91. 2 2
      cli/create-user.php
  92. 2 2
      cli/update-user.php
  93. 1 1
      cli/user-info.php
  94. 4 1
      composer.json
  95. 165 100
      composer.lock
  96. 8 1
      lib/Minz/ActionController.php
  97. 5 5
      lib/Minz/Configuration.php
  98. 1 1
      lib/Minz/ControllerNotExistException.php
  99. 0 1
      lib/Minz/Dispatcher.php
  100. 4 0
      lib/Minz/Mailer.php

+ 3 - 0
.github/workflows/tests.yml

@@ -46,6 +46,9 @@ jobs:
     - name: PHP_CodeSniffer
       run: composer run-script phpcs
 
+    - name: PHPStan
+      run: composer run-script phpstan
+
     # NPM tests
 
     - name: Uses Node.js

+ 4 - 4
app/Controllers/authController.php

@@ -22,7 +22,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 			Minz_Error::error(403);
 		}
 
-		Minz_View::prependTitle(_t('admin.auth.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('admin.auth.title') . ' · ');
 
 		if (Minz_Request::isPost()) {
 			$ok = true;
@@ -107,8 +107,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 	public function formLoginAction() {
 		invalidateHttpCache();
 
-		Minz_View::prependTitle(_t('gen.auth.login') . ' · ');
-		Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
+		FreshRSS_View::prependTitle(_t('gen.auth.login') . ' · ');
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
 
 		$limits = FreshRSS_Context::$system_conf->limits;
 		$this->view->cookie_days = round($limits['cookie_duration'] / 86400, 1);
@@ -237,6 +237,6 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 		$this->view->show_tos_checkbox = file_exists(join_path(DATA_PATH, 'tos.html'));
 		$this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation;
 		$this->view->preferred_language = Minz_Translate::getLanguage(null, Minz_Request::getPreferredLanguages(), FreshRSS_Context::$system_conf->language);
-		Minz_View::prependTitle(_t('gen.auth.registration.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('gen.auth.registration.title') . ' · ');
 	}
 }

+ 11 - 10
app/Controllers/configureController.php

@@ -70,7 +70,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 
 		$this->view->themes = FreshRSS_Themes::get();
 
-		Minz_View::prependTitle(_t('conf.display.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.display.title') . ' · ');
 	}
 
 	/**
@@ -134,7 +134,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'reading' ]);
 		}
 
-		Minz_View::prependTitle(_t('conf.reading.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.reading.title') . ' · ');
 	}
 
 	/**
@@ -148,8 +148,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 	 * some unwanted behavior when the end-user was using an ad-blocker.
 	 */
 	public function integrationAction() {
-		Minz_View::appendScript(Minz_Url::display('/scripts/integration.js?' . @filemtime(PUBLIC_PATH . '/scripts/integration.js')));
-		Minz_View::appendScript(Minz_Url::display('/scripts/draggable.js?' . @filemtime(PUBLIC_PATH . '/scripts/draggable.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/integration.js?' . @filemtime(PUBLIC_PATH . '/scripts/integration.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/draggable.js?' . @filemtime(PUBLIC_PATH . '/scripts/draggable.js')));
 
 		if (Minz_Request::isPost()) {
 			$params = $_POST;
@@ -160,7 +160,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'integration' ]);
 		}
 
-		Minz_View::prependTitle(_t('conf.sharing.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.sharing.title') . ' · ');
 	}
 
 	/**
@@ -191,7 +191,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			Minz_Request::good(_t('feedback.conf.shortcuts_updated'), array('c' => 'configure', 'a' => 'shortcut'));
 		}
 
-		Minz_View::prependTitle(_t('conf.shortcut.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.shortcut.title') . ' · ');
 	}
 
 	/**
@@ -264,7 +264,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->size_total = $databaseDAO->size(true);
 		}
 
-		Minz_View::prependTitle(_t('conf.archiving.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.archiving.title') . ' · ');
 	}
 
 	/**
@@ -278,7 +278,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 	 * checking if categories and feeds are still in use.
 	 */
 	public function queriesAction() {
-		Minz_View::appendScript(Minz_Url::display('/scripts/draggable.js?' . @filemtime(PUBLIC_PATH . '/scripts/draggable.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/draggable.js?' . @filemtime(PUBLIC_PATH . '/scripts/draggable.js')));
 
 		$category_dao = FreshRSS_Factory::createCategoryDao();
 		$feed_dao = FreshRSS_Factory::createFeedDao();
@@ -287,6 +287,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 		if (Minz_Request::isPost()) {
 			$params = Minz_Request::param('queries', array());
 
+			$queries = [];
 			foreach ($params as $key => $query) {
 				if (!$query['name']) {
 					$query['name'] = _t('conf.query.number', $key + 1);
@@ -319,7 +320,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->queryId = $id;
 		}
 
-		Minz_View::prependTitle(_t('conf.query.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.query.title') . ' · ');
 	}
 
 	/**
@@ -370,7 +371,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'queries', 'params' => ['id' => $id] ]);
 		}
 
-		Minz_View::prependTitle(_t('conf.query.title') . ' · ' . $query->getName() . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.query.title') . ' · ' . $query->getName() . ' · ');
 	}
 
 	/**

+ 8 - 1
app/Controllers/entryController.php

@@ -4,6 +4,13 @@
  * Controller to handle every entry actions.
  */
 class FreshRSS_entry_Controller extends Minz_ActionController {
+
+	/**
+	 * JavaScript request or not.
+	 * @var bool
+	 */
+	private $ajax = false;
+
 	/**
 	 * This action is called before every other action in that class. It is
 	 * the common boiler plate for every action. It is triggered by the
@@ -114,7 +121,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
 				'c' => 'index',
 				'a' => 'index',
 				'params' => $params,
-			), true);
+			));
 		}
 	}
 

+ 1 - 1
app/Controllers/errorController.php

@@ -63,6 +63,6 @@ class FreshRSS_error_Controller extends Minz_ActionController {
 			$this->view->errorMessage = $error_message;
 		}
 
-		Minz_View::prependTitle($this->view->code . ' · ');
+		FreshRSS_View::prependTitle($this->view->code . ' · ');
 	}
 }

+ 1 - 1
app/Controllers/extensionController.php

@@ -19,7 +19,7 @@ class FreshRSS_extension_Controller extends Minz_ActionController {
 	 * This action lists all the extensions available to the current user.
 	 */
 	public function indexAction() {
-		Minz_View::prependTitle(_t('admin.extensions.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('admin.extensions.title') . ' · ');
 		$this->view->extension_list = array(
 			'system' => array(),
 			'user' => array(),

+ 9 - 8
app/Controllers/feedController.php

@@ -190,19 +190,19 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 			} catch (FreshRSS_BadUrl_Exception $e) {
 				// Given url was not a valid url!
 				Minz_Log::warning($e->getMessage());
-				Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect);
+				return Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect);
 			} catch (FreshRSS_Feed_Exception $e) {
 				// Something went bad (timeout, server not found, etc.)
 				Minz_Log::warning($e->getMessage());
-				Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
+				return Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
 			} catch (Minz_FileNotExistException $e) {
 				// Cache directory doesn't exist!
 				Minz_Log::error($e->getMessage());
-				Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
+				return Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
 			} catch (FreshRSS_AlreadySubscribed_Exception $e) {
-				Minz_Request::bad(_t('feedback.sub.feed.already_subscribed', $e->feedName()), $url_redirect);
+				return Minz_Request::bad(_t('feedback.sub.feed.already_subscribed', $e->feedName()), $url_redirect);
 			} catch (FreshRSS_FeedNotAdded_Exception $e) {
-				Minz_Request::bad(_t('feedback.sub.feed.not_added', $e->feedName()), $url_redirect);
+				return Minz_Request::bad(_t('feedback.sub.feed.not_added', $e->feedName()), $url_redirect);
 			}
 
 			// Entries are in DB, we redirect to feed configuration page.
@@ -211,10 +211,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 			Minz_Request::good(_t('feedback.sub.feed.added', $feed->name()), $url_redirect);
 		} else {
 			// GET request: we must ask confirmation to user before adding feed.
-			Minz_View::prependTitle(_t('sub.feed.title_add') . ' · ');
+			FreshRSS_View::prependTitle(_t('sub.feed.title_add') . ' · ');
 
-			$this->catDAO = FreshRSS_Factory::createCategoryDao();
-			$this->view->categories = $this->catDAO->listCategories(false);
+			$catDAO = FreshRSS_Factory::createCategoryDao();
+			$this->view->categories = $catDAO->listCategories(false);
 			$this->view->feed = new FreshRSS_Feed($url);
 			try {
 				// We try to get more information about the feed.
@@ -567,6 +567,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 		$force = Minz_Request::param('force');
 		$maxFeeds = (int)Minz_Request::param('maxFeeds');
 		$noCommit = ($_POST['noCommit'] ?? 0) == 1;
+		$feed = null;
 
 		if ($id == -1 && !$noCommit) {	//Special request only to commit & refresh DB cache
 			$updated_feeds = 0;

+ 8 - 1
app/Controllers/importExportController.php

@@ -4,6 +4,11 @@
  * Controller to handle every import and export actions.
  */
 class FreshRSS_importExport_Controller extends Minz_ActionController {
+
+	private $catDAO;
+	private $entryDAO;
+	private $feedDAO;
+
 	/**
 	 * This action is called before every other action in that class. It is
 	 * the common boiler plate for every action. It is triggered by the
@@ -26,7 +31,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 	 */
 	public function indexAction() {
 		$this->view->feeds = $this->feedDAO->listFeeds();
-		Minz_View::prependTitle(_t('sub.import_export.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('sub.import_export.title') . ' · ');
 	}
 
 	private static function megabytes($size_str) {
@@ -504,6 +509,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			$website = $origin['htmlUrl'];
 		} elseif (!empty($origin['feedUrl'])) {
 			$website = $origin['feedUrl'];
+		} else {
+			$website = '';
 		}
 		$name = empty($origin['title']) ? $website : $origin['title'];
 

+ 10 - 10
app/Controllers/indexController.php

@@ -41,12 +41,12 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 
 		$this->view->categories = FreshRSS_Context::$categories;
 
-		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
+		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . FreshRSS_View::title();
 		$title = FreshRSS_Context::$name;
 		if (FreshRSS_Context::$get_unread > 0) {
 			$title = '(' . FreshRSS_Context::$get_unread . ') ' . $title;
 		}
-		Minz_View::prependTitle($title . ' · ');
+		FreshRSS_View::prependTitle($title . ' · ');
 
 		FreshRSS_Context::$id_max = time() . '000000';
 
@@ -104,8 +104,8 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 			return;
 		}
 
-		Minz_View::appendScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
-		Minz_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
 
 		try {
 			$this->updateContext();
@@ -115,12 +115,12 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 
 		$this->view->categories = FreshRSS_Context::$categories;
 
-		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
+		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . FreshRSS_View::title();
 		$title = _t('index.feed.title_global');
 		if (FreshRSS_Context::$get_unread > 0) {
 			$title = '(' . FreshRSS_Context::$get_unread . ') ' . $title;
 		}
-		Minz_View::prependTitle($title . ' · ');
+		FreshRSS_View::prependTitle($title . ' · ');
 
 		$this->_csp([
 			'default-src' => "'self'",
@@ -161,7 +161,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 
 		// No layout for RSS output.
 		$this->view->url = PUBLIC_TO_INDEX_PATH . '/' . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']);
-		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
+		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . FreshRSS_View::title();
 		$this->view->_layout(false);
 		header('Content-Type: application/rss+xml; charset=utf-8');
 	}
@@ -253,7 +253,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 	 * This action displays the about page of FreshRSS.
 	 */
 	public function aboutAction() {
-		Minz_View::prependTitle(_t('index.about.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('index.about.title') . ' · ');
 	}
 
 	/**
@@ -270,7 +270,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 
 		$this->view->terms_of_service = $terms_of_service;
 		$this->view->can_register = !max_registrations_reached();
-		Minz_View::prependTitle(_t('index.tos.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('index.tos.title') . ' · ');
 	}
 
 	/**
@@ -281,7 +281,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 			Minz_Error::error(403);
 		}
 
-		Minz_View::prependTitle(_t('index.log.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('index.log.title') . ' · ');
 
 		if (Minz_Request::isPost()) {
 			FreshRSS_LogDAO::truncate();

+ 7 - 3
app/Controllers/statsController.php

@@ -20,7 +20,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 			'style-src' => "'self' 'unsafe-inline'",
 		]);
 
-		Minz_View::prependTitle(_t('admin.stats.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('admin.stats.title') . ' · ');
 	}
 
 	private function convertToSerie($data) {
@@ -57,7 +57,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 	 */
 	public function indexAction() {
 		$statsDAO = FreshRSS_Factory::createStatsDAO();
-		Minz_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
 
 		$this->view->repartition = $statsDAO->calculateEntryRepartition();
 
@@ -66,6 +66,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$this->view->average = round(array_sum(array_values($entryCount)) / count($entryCount), 2);
 
 		$feedByCategory_calculated = $statsDAO->calculateFeedByCategory();
+		$feedByCategory = [];
 		for ($i = 0; $i < count($feedByCategory_calculated); $i++) {
 			$feedByCategory['label'][$i] 	= $feedByCategory_calculated[$i]['label'];
 			$feedByCategory['data'][$i] 	= $feedByCategory_calculated[$i]['data'];
@@ -73,6 +74,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$this->view->feedByCategory = $feedByCategory;
 
 		$entryByCategory_calculated = $statsDAO->calculateEntryByCategory();
+		$entryByCategory = [];
 		for ($i = 0; $i < count($entryByCategory_calculated); $i++) {
 			$entryByCategory['label'][$i] 	= $entryByCategory_calculated[$i]['label'];
 			$entryByCategory['data'][$i] 	= $entryByCategory_calculated[$i]['data'];
@@ -81,6 +83,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 
 		$this->view->topFeed = $statsDAO->calculateTopFeed();
 
+		$last30DaysLabels = [];
 		for ($i = 0; $i < 30; $i++) {
 			$last30DaysLabels[$i] = date('d.m.Y', strtotime((-30 + $i) . ' days'));
 		}
@@ -178,7 +181,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$categoryDAO 	= FreshRSS_Factory::createCategoryDao();
 		$feedDAO 		= FreshRSS_Factory::createFeedDao();
 
-		Minz_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
 
 		$id = Minz_Request::param('id', null);
 
@@ -198,6 +201,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$this->view->repartitionMonth 		= $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id);
 		$this->view->averageMonth 			= $statsDAO->calculateEntryAveragePerFeedPerMonth($id);
 
+		$hours24Labels = [];
 		for ($i = 0; $i < 24; $i++) {
 			$hours24Labels[$i] = $i . ':xx';
 		}

+ 5 - 5
app/Controllers/subscriptionController.php

@@ -44,8 +44,8 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 	 * It displays categories and associated feeds.
 	 */
 	public function indexAction() {
-		Minz_View::appendScript(Minz_Url::display('/scripts/category.js?' . @filemtime(PUBLIC_PATH . '/scripts/category.js')));
-		Minz_View::prependTitle(_t('sub.title') . ' · ');
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/category.js?' . @filemtime(PUBLIC_PATH . '/scripts/category.js')));
+		FreshRSS_View::prependTitle(_t('sub.title') . ' · ');
 
 		$this->view->onlyFeedsWithError = Minz_Request::paramTernary('error');
 
@@ -104,7 +104,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 		$feed = $this->view->feeds[$id];
 		$this->view->feed = $feed;
 
-		Minz_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $feed->name() . ' · ');
+		FreshRSS_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $feed->name() . ' · ');
 
 		if (Minz_Request::isPost()) {
 			$user = trim(Minz_Request::param('http_user_feed' . $id, ''));
@@ -282,13 +282,13 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 	 * This action displays the bookmarklet page.
 	 */
 	public function bookmarkletAction() {
-		Minz_View::prependTitle(_t('sub.title.subscription_tools') . ' . ');
+		FreshRSS_View::prependTitle(_t('sub.title.subscription_tools') . ' . ');
 	}
 
 	/**
 	 * This action displays the page to add a new feed
 	 */
 	public function addAction() {
-		Minz_View::prependTitle(_t('sub.title.add') . ' . ');
+		FreshRSS_View::prependTitle(_t('sub.title.add') . ' . ');
 	}
 }

+ 10 - 3
app/Controllers/tagController.php

@@ -4,6 +4,13 @@
  * Controller to handle every tag actions.
  */
 class FreshRSS_tag_Controller extends Minz_ActionController {
+
+	/**
+	 * JavaScript request or not.
+	 * @var bool
+	 */
+	private $ajax = false;
+
 	/**
 	 * This action is called before every other action in that class. It is
 	 * the common boiler plate for every action. It is triggered by the
@@ -92,10 +99,10 @@ class FreshRSS_tag_Controller extends Minz_ActionController {
 		$tagDAO = FreshRSS_Factory::createTagDao();
 		if (strlen($name) > 0 && null === $tagDAO->searchByName($name)) {
 			$tagDAO->addTag(['name' => $name]);
-			Minz_Request::good(_t('feedback.tag.created', $name), ['c' => 'tag', 'a' => 'index'], true);
+			Minz_Request::good(_t('feedback.tag.created', $name), ['c' => 'tag', 'a' => 'index']);
 		}
 
-		Minz_Request::bad(_t('feedback.tag.name_exists', $name), ['c' => 'tag', 'a' => 'index'], true);
+		Minz_Request::bad(_t('feedback.tag.name_exists', $name), ['c' => 'tag', 'a' => 'index']);
 	}
 
 	public function renameAction() {
@@ -123,7 +130,7 @@ class FreshRSS_tag_Controller extends Minz_ActionController {
 			$tagDAO->deleteTag($sourceId);
 		}
 
-		Minz_Request::good(_t('feedback.tag.renamed', $sourceName, $targetName), ['c' => 'tag', 'a' => 'index'], true);
+		Minz_Request::good(_t('feedback.tag.renamed', $sourceName, $targetName), ['c' => 'tag', 'a' => 'index']);
 	}
 
 	public function indexAction() {

+ 6 - 2
app/Controllers/updateController.php

@@ -108,7 +108,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 	}
 
 	public function indexAction() {
-		Minz_View::prependTitle(_t('admin.update.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('admin.update.title') . ' · ');
 
 		if (file_exists(UPDATE_FILENAME)) {
 			// There is an update file to apply!
@@ -224,6 +224,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 				$res = !self::hasGitUpdate();
 			} else {
 				require(UPDATE_FILENAME);
+				// @phpstan-ignore-next-line
 				$res = do_post_update();
 			}
 
@@ -244,9 +245,12 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 			} else {
 				require(UPDATE_FILENAME);
 				if (Minz_Request::isPost()) {
+					// @phpstan-ignore-next-line
 					save_info_update();
 				}
+				// @phpstan-ignore-next-line
 				if (!need_info_update()) {
+					// @phpstan-ignore-next-line
 					$res = apply_update();
 				} else {
 					return;
@@ -269,7 +273,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 	 * This action displays information about installation.
 	 */
 	public function checkInstallAction() {
-		Minz_View::prependTitle(_t('admin.check_install.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('admin.check_install.title') . ' · ');
 
 		$this->view->status_php = check_install_php();
 		$this->view->status_files = check_install_files();

+ 7 - 6
app/Controllers/userController.php

@@ -95,9 +95,9 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			$this->view->disable_aside = true;
 		}
 
-		Minz_View::prependTitle(_t('conf.profile.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('conf.profile.title') . ' · ');
 
-		Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
+		FreshRSS_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
 
 		if (Minz_Request::isPost()) {
 			$system_conf = FreshRSS_Context::$system_conf;
@@ -173,7 +173,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			Minz_Error::error(403);
 		}
 
-		Minz_View::prependTitle(_t('admin.user.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('admin.user.title') . ' · ');
 
 		if (Minz_Request::isPost()) {
 			$action = Minz_Request::param('action');
@@ -227,6 +227,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 
 		$ok = self::checkUsername($new_user_name);
 		$homeDir = join_path(DATA_PATH, 'users', $new_user_name);
+		$configPath = '';
 
 		if ($ok) {
 			$languages = Minz_Translate::availableLanguages();
@@ -418,7 +419,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			Minz_Error::error(404);
 		}
 
-		Minz_View::prependTitle(_t('user.email.validation.title') . ' · ');
+		FreshRSS_View::prependTitle(_t('user.email.validation.title') . ' · ');
 		$this->view->_layout('simple');
 
 		$username = Minz_Request::param('username');
@@ -429,11 +430,11 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 		} elseif (FreshRSS_Auth::hasAccess()) {
 			$user_config = FreshRSS_Context::$user_conf;
 		} else {
-			Minz_Error::error(403);
+			return Minz_Error::error(403);
 		}
 
 		if (!FreshRSS_UserDAO::exists($username) || $user_config === null) {
-			Minz_Error::error(404);
+			return Minz_Error::error(404);
 		}
 
 		if ($user_config->email_validation_token === '') {

+ 7 - 5
app/FreshRSS.php

@@ -24,6 +24,8 @@ class FreshRSS extends Minz_FrontController {
 			Minz_Session::init('FreshRSS');
 		}
 
+		Minz_ActionController::$viewType = 'FreshRSS_View';
+
 		FreshRSS_Context::initSystem();
 		if (FreshRSS_Context::$system_conf == null) {
 			$message = 'Error during context system init!';
@@ -105,7 +107,7 @@ class FreshRSS extends Minz_FrontController {
 					case '.js':
 						$theme_id = $theme['id'];
 						$filename = $file;
-						Minz_View::prependScript(Minz_Url::display(FreshRSS::getThemeFileUrl($theme_id, $filename)));
+						FreshRSS_View::prependScript(Minz_Url::display(FreshRSS::getThemeFileUrl($theme_id, $filename)));
 						break;
 					case '.css':
 					default:
@@ -120,21 +122,21 @@ class FreshRSS extends Minz_FrontController {
 							$filename = substr($filename, 0, -4);
 							$filename = $filename . '.rtl.css';
 						}
-						Minz_View::prependStyle(Minz_Url::display(FreshRSS::getThemeFileUrl($theme_id, $filename)));
+						FreshRSS_View::prependStyle(Minz_Url::display(FreshRSS::getThemeFileUrl($theme_id, $filename)));
 				}
 			}
 		}
 		//Use prepend to insert before extensions. Added in reverse order.
 		if (Minz_Request::controllerName() !== 'index') {
-			Minz_View::prependScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
+			FreshRSS_View::prependScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
 		}
-		Minz_View::prependScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js')));
+		FreshRSS_View::prependScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js')));
 	}
 
 	private static function loadNotifications() {
 		$notif = Minz_Request::getNotification();
 		if ($notif) {
-			Minz_View::_param('notification', $notif);
+			FreshRSS_View::_param('notification', $notif);
 		}
 	}
 

+ 4 - 2
app/Models/DatabaseDAO.php

@@ -24,7 +24,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
 		$db = FreshRSS_Context::$system_conf->db;
 
 		try {
-			$sql = sprintf($SQL_CREATE_DB, empty($db['base']) ? '' : $db['base']);
+			$sql = sprintf($GLOBALS['SQL_CREATE_DB'], empty($db['base']) ? '' : $db['base']);
 			return $this->pdo->exec($sql) === false ? 'Error during CREATE DATABASE' : '';
 		} catch (Exception $e) {
 			syslog(LOG_DEBUG, __method__ . ' notice: ' . $e->getMessage());
@@ -176,7 +176,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
 
 			$ok = false;
 			try {
-				$ok = $this->pdo->exec($SQL_UPDATE_GUID_LATIN1_BIN) !== false;	//FreshRSS 1.12
+				$ok = $this->pdo->exec($GLOBALS['SQL_UPDATE_GUID_LATIN1_BIN']) !== false;	//FreshRSS 1.12
 			} catch (Exception $e) {
 				$ok = false;
 				Minz_Log::error(__METHOD__ . ' error: ' . $e->getMessage());
@@ -277,6 +277,8 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
 				$entryFrom = $entryDAOSQLite; $entryTo = $entryDAO;
 				$tagFrom = $tagDAOSQLite; $tagTo = $tagDAO;
 				break;
+			default:
+				return;
 		}
 
 		$idMaps = [];

+ 1 - 1
app/Models/EntryDAO.php

@@ -32,7 +32,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		try {
 			require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php');
 			Minz_Log::warning('SQL CREATE TABLE entrytmp...');
-			$ok = $this->pdo->exec($SQL_CREATE_TABLE_ENTRYTMP . $SQL_CREATE_INDEX_ENTRY_1) !== false;
+			$ok = $this->pdo->exec($GLOBALS['SQL_CREATE_TABLE_ENTRYTMP'] . $GLOBALS['SQL_CREATE_INDEX_ENTRY_1']) !== false;
 		} catch (Exception $ex) {
 			Minz_Log::error(__method__ . ' error: ' . $ex->getMessage());
 		}

+ 0 - 1
app/Models/Search.php

@@ -570,7 +570,6 @@ class FreshRSS_Search {
 	 * Supported delimiters are single quote (') and double quotes (").
 	 *
 	 * @param string $input
-	 * @return string
 	 */
 	private function parseSearch($input) {
 		$input = self::cleanSearch($input);

+ 1 - 1
app/Models/TagDAO.php

@@ -20,7 +20,7 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$databaseDAO->ensureCaseInsensitiveGuids();
 
 			Minz_Log::warning('SQL CREATE TABLE tag...');
-			$ok = $this->pdo->exec($SQL_CREATE_TABLE_TAGS) !== false;
+			$ok = $this->pdo->exec($GLOBALS['SQL_CREATE_TABLE_TAGS']) !== false;
 		} catch (Exception $e) {
 			Minz_Log::error('FreshRSS_EntryDAO::createTagTable error: ' . $e->getMessage());
 		}

+ 3 - 2
app/Models/UserDAO.php

@@ -5,9 +5,10 @@ class FreshRSS_UserDAO extends Minz_ModelPdo {
 		require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php');
 
 		try {
-			$sql = $SQL_CREATE_TABLES . $SQL_CREATE_TABLE_ENTRYTMP . $SQL_CREATE_TABLE_TAGS;
+			$sql = $GLOBALS['$SQL_CREATE_TABLES'] . $GLOBALS['SQL_CREATE_TABLE_ENTRYTMP'] . $GLOBALS['SQL_CREATE_TABLE_TAGS'];
 			$ok = $this->pdo->exec($sql) !== false;	//Note: Only exec() can take multiple statements safely.
 		} catch (Exception $e) {
+			$ok = false;
 			Minz_Log::error('Error while creating database for user ' . $this->current_user . ': ' . $e->getMessage());
 		}
 
@@ -26,7 +27,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo {
 		}
 
 		require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php');
-		$ok = $this->pdo->exec($SQL_DROP_TABLES) !== false;
+		$ok = $this->pdo->exec($GLOBALS['SQL_DROP_TABLES']) !== false;
 
 		if ($ok) {
 			return true;

+ 119 - 0
app/Models/View.php

@@ -0,0 +1,119 @@
+<?php
+
+class FreshRSS_View extends Minz_View {
+
+	// Main views
+	public $callbackBeforeEntries;
+	public $callbackBeforePagination;
+	public $categories;
+	public $category;
+	public $entries;
+	public $entry;
+	public $feed;
+	public $feeds;
+	public $nbUnreadTags;
+	public $tags;
+
+	// Substriptions
+	public $default_category;
+	public $displaySlider;
+	public $load_ok;
+	public $onlyFeedsWithError;
+	public $signalError;
+
+	// Manage users
+	public $details;
+	public $disable_aside;
+	public $show_email_field;
+	public $username;
+	public $users;
+
+	// Updates
+	public $last_update_time;
+	public $status_files;
+	public $status_php;
+	public $update_to_apply;
+
+	// Archiving
+	public $nb_total;
+	public $size_total;
+	public $size_user;
+
+	// Display
+	public $themes;
+
+	// Shortcuts
+	public $list_keys;
+
+	// User queries
+	public $queries;
+	public $query;
+
+	// Export / Import
+	public $content;
+	public $entriesRaw;
+	public $entryIdsTagNames;
+	public $list_title;
+	public $queryId;
+	public $type;
+
+	// Form login
+	public $cookie_days;
+	public $nonce;
+	public $salt1;
+
+	// Registration
+	public $can_register;
+	public $preferred_language;
+	public $show_tos_checkbox;
+	public $terms_of_service;
+
+	// Email validation
+	public $site_title;
+	public $validation_url;
+
+	// Logs
+	public $currentPage;
+	public $logsPaginator;
+	public $nbPage;
+
+	// RSS view
+	public $rss_title;
+	public $url;
+
+	// Content preview
+	public $fatalError;
+	public $htmlContent;
+	public $selectorSuccess;
+
+	// Extensions
+	public $ext_details;
+	public $extension_list;
+	public $extension;
+	public $extensions_installed;
+
+	// Errors
+	public $code;
+	public $errorMessage;
+
+	// Statistics
+	public $average;
+	public $averageDayOfWeek;
+	public $averageHour;
+	public $averageMonth;
+	public $days;
+	public $entryByCategory;
+	public $entryCount;
+	public $feedByCategory;
+	public $hours24Labels;
+	public $idleFeeds;
+	public $last30DaysLabel;
+	public $last30DaysLabels;
+	public $months;
+	public $repartition;
+	public $repartitionDayOfWeek;
+	public $repartitionHour;
+	public $repartitionMonth;
+	public $topFeed;
+
+}

+ 7 - 7
app/SQL/install.sql.mysql.php

@@ -1,9 +1,9 @@
 <?php
-$SQL_CREATE_DB = <<<'SQL'
+$GLOBALS['SQL_CREATE_DB'] = <<<'SQL'
 CREATE DATABASE IF NOT EXISTS `%1$s` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
 SQL;
 
-$SQL_CREATE_TABLES = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLES'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `_category` (
 	`id` SMALLINT NOT NULL AUTO_INCREMENT,	-- v0.7
 	`name` VARCHAR(191) NOT NULL,	-- Max index length for Unicode is 191 characters (767 bytes) FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE
@@ -64,11 +64,11 @@ ENGINE = INNODB;
 INSERT IGNORE INTO `_category` (id, name) VALUES(1, "Uncategorized");
 SQL;
 
-$SQL_CREATE_INDEX_ENTRY_1 = <<<'SQL'
+$GLOBALS['SQL_CREATE_INDEX_ENTRY_1'] = <<<'SQL'
 CREATE INDEX `entry_feed_read_index` ON `_entry` (`id_feed`,`is_read`);	-- v1.7
 SQL;
 
-$SQL_CREATE_TABLE_ENTRYTMP = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLE_ENTRYTMP'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `_entrytmp` (	-- v1.7
 	`id` BIGINT NOT NULL,
 	`guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
@@ -91,7 +91,7 @@ CREATE TABLE IF NOT EXISTS `_entrytmp` (	-- v1.7
 ENGINE = INNODB;
 SQL;
 
-$SQL_CREATE_TABLE_TAGS = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLE_TAGS'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `_tag` (	-- v1.12
 	`id` SMALLINT NOT NULL AUTO_INCREMENT,
 	`name` VARCHAR(63) NOT NULL,
@@ -112,11 +112,11 @@ CREATE TABLE IF NOT EXISTS `_entrytag` (	-- v1.12
 ENGINE = INNODB;
 SQL;
 
-$SQL_DROP_TABLES = <<<'SQL'
+$GLOBALS['SQL_DROP_TABLES'] = <<<'SQL'
 DROP TABLE IF EXISTS `_entrytag`, `_tag`, `_entrytmp`, `_entry`, `_feed`, `_category`;
 SQL;
 
-$SQL_UPDATE_GUID_LATIN1_BIN = <<<'SQL'
+$GLOBALS['SQL_UPDATE_GUID_LATIN1_BIN'] = <<<'SQL'
 ALTER TABLE `_entrytmp` MODIFY `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL;	-- v1.12
 ALTER TABLE `_entry` MODIFY `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL;
 SQL;

+ 6 - 6
app/SQL/install.sql.pgsql.php

@@ -1,9 +1,9 @@
 <?php
-$SQL_CREATE_DB = <<<'SQL'
+$GLOBALS['SQL_CREATE_DB'] = <<<'SQL'
 CREATE DATABASE "%1$s" ENCODING 'UTF8';
 SQL;
 
-$SQL_CREATE_TABLES = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLES'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `_category` (
 	"id" SERIAL PRIMARY KEY,
 	"name" VARCHAR(255) UNIQUE NOT NULL,
@@ -59,11 +59,11 @@ INSERT INTO `_category` (id, name)
 	RETURNING nextval('`_category_id_seq`');
 SQL;
 
-$SQL_CREATE_INDEX_ENTRY_1 = <<<'SQL'
+$GLOBALS['SQL_CREATE_INDEX_ENTRY_1'] = <<<'SQL'
 CREATE INDEX IF NOT EXISTS `_entry_feed_read_index` ON `_entry` ("id_feed","is_read");	-- v1.7
 SQL;
 
-$SQL_CREATE_TABLE_ENTRYTMP = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLE_ENTRYTMP'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `_entrytmp` (	-- v1.7
 	"id" BIGINT NOT NULL PRIMARY KEY,
 	"guid" VARCHAR(760) NOT NULL,
@@ -84,7 +84,7 @@ CREATE TABLE IF NOT EXISTS `_entrytmp` (	-- v1.7
 CREATE INDEX IF NOT EXISTS `_entrytmp_date_index` ON `_entrytmp` ("date");
 SQL;
 
-$SQL_CREATE_TABLE_TAGS = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLE_TAGS'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `_tag` (	-- v1.12
 	"id" SERIAL PRIMARY KEY,
 	"name" VARCHAR(63) UNIQUE NOT NULL,
@@ -100,6 +100,6 @@ CREATE TABLE IF NOT EXISTS `_entrytag` (
 CREATE INDEX IF NOT EXISTS `_entrytag_id_entry_index` ON `_entrytag` ("id_entry");
 SQL;
 
-$SQL_DROP_TABLES = <<<'SQL'
+$GLOBALS['SQL_DROP_TABLES'] = <<<'SQL'
 DROP TABLE IF EXISTS `_entrytag`, `_tag`, `_entrytmp`, `_entry`, `_feed`, `_category`;
 SQL;

+ 6 - 6
app/SQL/install.sql.sqlite.php

@@ -1,9 +1,9 @@
 <?php
-$SQL_CREATE_DB = <<<'SQL'
+$GLOBALS['SQL_CREATE_DB'] = <<<'SQL'
 SELECT 1;	-- Do nothing for SQLite
 SQL;
 
-$SQL_CREATE_TABLES = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLES'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `category` (
 	`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 	`name` VARCHAR(255) NOT NULL,
@@ -59,11 +59,11 @@ CREATE INDEX IF NOT EXISTS entry_feed_read_index ON `entry`(`id_feed`,`is_read`)
 INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "Uncategorized");
 SQL;
 
-$SQL_CREATE_INDEX_ENTRY_1 = <<<'SQL'
+$GLOBALS['SQL_CREATE_INDEX_ENTRY_1'] = <<<'SQL'
 CREATE INDEX IF NOT EXISTS entry_feed_read_index ON `entry`(`id_feed`,`is_read`);	-- v1.7
 SQL;
 
-$SQL_CREATE_TABLE_ENTRYTMP = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLE_ENTRYTMP'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `entrytmp` (	-- v1.7
 	`id` BIGINT NOT NULL,
 	`guid` VARCHAR(760) NOT NULL,
@@ -85,7 +85,7 @@ CREATE TABLE IF NOT EXISTS `entrytmp` (	-- v1.7
 CREATE INDEX IF NOT EXISTS entrytmp_date_index ON `entrytmp`(`date`);
 SQL;
 
-$SQL_CREATE_TABLE_TAGS = <<<'SQL'
+$GLOBALS['SQL_CREATE_TABLE_TAGS'] = <<<'SQL'
 CREATE TABLE IF NOT EXISTS `tag` (	-- v1.12
 	`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 	`name` VARCHAR(63) NOT NULL,
@@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS `entrytag` (
 CREATE INDEX IF NOT EXISTS entrytag_id_entry_index ON `entrytag` (`id_entry`);
 SQL;
 
-$SQL_DROP_TABLES = <<<'SQL'
+$GLOBALS['SQL_DROP_TABLES'] = <<<'SQL'
 DROP TABLE IF EXISTS `entrytag`;
 DROP TABLE IF EXISTS `tag`;
 DROP TABLE IF EXISTS `entrytmp`;

+ 3 - 3
app/Services/ExportService.php

@@ -41,7 +41,7 @@ class FreshRSS_Export_Service {
 	public function generateOpml() {
 		require_once(LIB_PATH . '/lib_opml.php');
 
-		$view = new Minz_View();
+		$view = new FreshRSS_View();
 		$day = date('Y-m-d');
 		$categories = [];
 
@@ -72,7 +72,7 @@ class FreshRSS_Export_Service {
 	 * @return array First item is the filename, second item is the content
 	 */
 	public function generateStarredEntries($type) {
-		$view = new Minz_View();
+		$view = new FreshRSS_View();
 		$view->categories = $this->category_dao->listCategories();
 		$day = date('Y-m-d');
 
@@ -108,7 +108,7 @@ class FreshRSS_Export_Service {
 			return null;
 		}
 
-		$view = new Minz_View();
+		$view = new FreshRSS_View();
 		$view->categories = $this->category_dao->listCategories();
 		$view->feed = $feed;
 		$day = date('Y-m-d');

+ 14 - 14
app/actualize_script.php

@@ -2,20 +2,6 @@
 <?php
 require(__DIR__ . '/../cli/_cli.php');
 
-/**
- * Writes to FreshRSS admin log, and if it is not already done by default,
- * writes to syslog (only if simplepie_syslog_enabled in FreshRSS configuration) and to STDOUT
- */
-function notice($message) {
-	Minz_Log::notice($message, ADMIN_LOG);
-	if (!COPY_LOG_TO_SYSLOG && SIMPLEPIE_SYSLOG_ENABLED) {
-		syslog(LOG_NOTICE, $message);
-	}
-	if (defined('STDOUT') && !COPY_SYSLOG_TO_STDERR) {
-		fwrite(STDOUT, $message . "\n");	//Unbuffered
-	}
-}
-
 session_cache_limiter('');
 ob_implicit_flush(false);
 ob_start();
@@ -35,6 +21,20 @@ FreshRSS_Context::initSystem();
 FreshRSS_Context::$system_conf->auth_type = 'none';  // avoid necessity to be logged in (not saved!)
 define('SIMPLEPIE_SYSLOG_ENABLED', FreshRSS_Context::$system_conf->simplepie_syslog_enabled);
 
+/**
+ * Writes to FreshRSS admin log, and if it is not already done by default,
+ * writes to syslog (only if simplepie_syslog_enabled in FreshRSS configuration) and to STDOUT
+ */
+function notice($message) {
+	Minz_Log::notice($message, ADMIN_LOG);
+	if (!COPY_LOG_TO_SYSLOG && SIMPLEPIE_SYSLOG_ENABLED) {
+		syslog(LOG_NOTICE, $message);
+	}
+	if (defined('STDOUT') && !COPY_SYSLOG_TO_STDERR) {
+		fwrite(STDOUT, $message . "\n");	//Unbuffered
+	}
+}
+
 notice('FreshRSS starting feeds actualization at ' . $begin_date->format('c'));
 
 // make sure the PHP setup of the CLI environment is compatible with FreshRSS as well

+ 1 - 0
app/layout/aside_feed.phtml

@@ -1,4 +1,5 @@
 <?php
+	/** @var FreshRSS_View $this */
 	$actual_view = Minz_Request::actionName();
 	$class = '';
 	if (FreshRSS_Context::$user_conf->hide_read_feeds &&

+ 7 - 4
app/layout/layout.phtml

@@ -1,4 +1,7 @@
-<?php FreshRSS::preLayout(); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	FreshRSS::preLayout();
+?>
 <!DOCTYPE html>
 <html lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>"<?php
 if (_t('gen.dir') === 'rtl') {
@@ -8,11 +11,11 @@ if (_t('gen.dir') === 'rtl') {
 	<head>
 		<meta charset="UTF-8" />
 		<meta name="viewport" content="initial-scale=1.0" />
-		<?= self::headStyle() ?>
+		<?= FreshRSS_View::headStyle() ?>
 		<script id="jsonVars" type="application/json">
 <?php $this->renderHelper('javascript_vars'); ?>
 		</script>
-		<?= self::headScript() ?>
+		<?= FreshRSS_View::headScript() ?>
 		<link rel="manifest" href="<?= Minz_Url::display('/themes/manifest.json') ?>" />
 		<link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="<?= Minz_Url::display('/favicon.ico') ?>" />
 		<link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="<?= Minz_Url::display('/themes/icons/favicon-256.png') ?>" />
@@ -25,7 +28,7 @@ if (_t('gen.dir') === 'rtl') {
 <?php if (!FreshRSS_Context::$system_conf->allow_referrer) { ?>
 		<meta name="referrer" content="never" />
 <?php } ?>
-		<?= self::headTitle() ?>
+		<?= FreshRSS_View::headTitle() ?>
 <?php
 	$url_base = Minz_Request::currentRequest();
 	if (isset($this->rss_title)) {

+ 7 - 4
app/layout/simple.phtml

@@ -1,14 +1,17 @@
-<?php FreshRSS::preLayout(); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	FreshRSS::preLayout();
+?>
 <!DOCTYPE html>
 <html lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>">
 	<head>
 		<meta charset="UTF-8" />
 		<meta name="viewport" content="initial-scale=1.0" />
-		<?= self::headStyle() ?>
+		<?= FreshRSS_View::headStyle() ?>
 		<script id="jsonVars" type="application/json">
 <?php $this->renderHelper('javascript_vars'); ?>
 		</script>
-		<?= self::headScript() ?>
+		<?= FreshRSS_View::headScript() ?>
 		<link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="<?= Minz_Url::display('/favicon.ico') ?>" />
 		<link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="<?= Minz_Url::display('/themes/icons/favicon-256.png') ?>" />
 		<link rel="apple-touch-icon" href="<?= Minz_Url::display('/themes/icons/apple-touch-icon.png') ?>" />
@@ -18,7 +21,7 @@
 		<meta name="msapplication-TileColor" content="#FFF" />
 		<meta name="referrer" content="never" />
 		<meta name="robots" content="noindex,nofollow" />
-		<?= self::headTitle() ?>
+		<?= FreshRSS_View::headTitle() ?>
 	</head>
 	<body>
 

+ 1 - 0
app/views/auth/formLogin.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <main class="prompt">
 	<h1><?= _t('gen.auth.login') ?></h1>
 

+ 4 - 1
app/views/auth/index.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 1 - 0
app/views/auth/register.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <main class="prompt">
 	<h1><?= _t('gen.auth.registration') ?></h1>
 

+ 5 - 2
app/views/configure/archiving.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
@@ -7,7 +10,7 @@
 
 	<h1><?= _t('conf.archiving') ?></h1>
 	<p class="help"><?= _i('help') ?> <?= _t('conf.archiving.help') ?></p>
-	
+
 	<form method="post" action="<?= _url('configure', 'archiving') ?>">
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
 

+ 5 - 2
app/views/configure/display.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
@@ -6,7 +9,7 @@
 	</div>
 
 	<h1><?= _t('conf.display') ?></h1>
-	
+
 	<form method="post" action="<?= _url('configure', 'display') ?>">
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
 

+ 5 - 2
app/views/configure/integration.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
@@ -24,7 +27,7 @@
 			<a target="_blank" rel="noreferrer" class="btn" title="<?= _t('conf.sharing.more_information') ?>" href="##help##"><?= _i('help') ?></a>
 			</div></div>' class="draggableList">
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
-		
+
 		<?php
 			foreach (FreshRSS_Context::$user_conf->sharing as $key => $share_options) {
 				$share = FreshRSS_Share::get($share_options['type']);

+ 4 - 1
app/views/configure/queries.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 1 - 0
app/views/configure/query.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 
 if ($this->query) {

+ 6 - 3
app/views/configure/reading.phtml

@@ -1,15 +1,18 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
 		<a class="link-back" href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a>
 	</div>
-	
+
 	<h1><?= _t('conf.reading') ?></h1>
 
 	<form method="post" action="<?= _url('configure', 'reading') ?>">
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
-		
+
 		<div class="form-group">
 			<label class="group-name" for="posts_per_page"><?= _t('conf.reading.articles_per_page') ?></label>
 			<div class="group-controls">

+ 5 - 2
app/views/configure/shortcut.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
@@ -25,7 +28,7 @@
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
 
 		<noscript><p class="alert alert-error"><?= _t('conf.shortcut.javascript') ?></p></noscript>
-		
+
 		<fieldset>
 			<legend><?= _t('conf.shortcut.views') ?></legend>
 

+ 4 - 1
app/views/configure/system.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

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

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 header('Content-Type: application/json; charset=UTF-8');
 

+ 1 - 0
app/views/entry/read.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 header('Content-Type: application/json; charset=UTF-8');
 

+ 3 - 2
app/views/error/index.phtml

@@ -1,11 +1,12 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <main class="post">
 	<div class="alert alert-error">
 		<h1 class="alert-head"><?= $this->code ?></h1>
 		<p>
 			<?= htmlspecialchars($this->errorMessage, ENT_NOQUOTES, 'UTF-8') ?>
 		</p>
-		<p>	
-			<?php if (FreshRSS_Auth::hasAccess()) {?>	
+		<p>
+			<?php if (FreshRSS_Auth::hasAccess()) {?>
 			<a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a>
 			<?php } else { ?>
 			<a href="<?= _url('auth', 'login') ?>"><?= _t('gen.auth.login') ?></a>

+ 1 - 1
app/views/extension/configure.phtml

@@ -1,3 +1,3 @@
 <?php
-
+/** @var FreshRSS_View $this */
 $this->renderHelper('extension/configure');

+ 4 - 1
app/views/extension/index.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 1 - 0
app/views/feed/add.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php if ($this->feed) { ?>
 <main class="post">
 	<h1><?= _t('sub.feed.add') ?></h1>

+ 5 - 2
app/views/feed/contentSelectorPreview.phtml

@@ -1,8 +1,11 @@
-<?php FreshRSS::preLayout(); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	FreshRSS::preLayout();
+?>
 <!DOCTYPE html>
 <html class="preview_background" lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>">
 	<head>
-		<?= self::headStyle() ?>
+		<?= FreshRSS_View::headStyle() ?>
 		<script src="<?= Minz_Url::display('/scripts/preview.js?' . @filemtime(PUBLIC_PATH . '/scripts/preview.js')) ?>"></script>
 	</head>
 	<body class="preview_background">

+ 2 - 1
app/views/helpers/category/update.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <div class="post">
 	<h2><?= $this->category->name() ?></h2>
 
@@ -30,7 +31,7 @@
 			<div class="group-controls">
 				<button class="btn btn-important"><?= _t('gen.action.submit') ?></button>
 				<button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button>
-				
+
 			</div>
 		</div>
 

+ 1 - 0
app/views/helpers/configure/query.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <div class="post">
 	<h2><?= $this->query->getName() ?></h2>
 

+ 1 - 0
app/views/helpers/export/articles.phtml

@@ -1,4 +1,5 @@
 <?php
+/** @var FreshRSS_View $this */
 $username = Minz_Session::param('currentUser', '_');
 
 $options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;

+ 1 - 0
app/views/helpers/export/opml.phtml

@@ -1,4 +1,5 @@
 <?php
+/** @var FreshRSS_View $this */
 
 $opml_array = array(
 	'head' => array(

+ 1 - 0
app/views/helpers/extension/configure.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <div class="post">
 	<h2>
 		<?= $this->extension->getName() ?> (<?= $this->extension->getVersion() ?>) —

+ 1 - 0
app/views/helpers/extension/details.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <ul class="horizontal-list">
 	<li class="item">
 		<?php if ($this->ext_details->getType() === 'user' || FreshRSS_Auth::hasAccess('admin')) { ?>

+ 1 - 0
app/views/helpers/feed/update.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <div class="post">
 	<h1><?= $this->feed->name() ?></h1>
 

+ 1 - 0
app/views/helpers/index/normal/entry_bottom.phtml

@@ -1,4 +1,5 @@
 <?php
+	/** @var FreshRSS_View $this */
 	$sharing = array();
 	if (FreshRSS_Auth::hasAccess()) {
 		$sharing = FreshRSS_Context::$user_conf->sharing;

+ 1 - 0
app/views/helpers/index/normal/entry_header.phtml

@@ -1,4 +1,5 @@
 <?php
+	/** @var FreshRSS_View $this */
 	$topline_read = FreshRSS_Context::$user_conf->topline_read;
 	$topline_favorite = FreshRSS_Context::$user_conf->topline_favorite;
 	$topline_thumbnail = FreshRSS_Context::$user_conf->topline_thumbnail;

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

@@ -1,4 +1,5 @@
 <?php
+/** @var FreshRSS_View $this */
 $mark = FreshRSS_Context::$user_conf->mark_when;
 $s = FreshRSS_Context::$user_conf->shortcuts;
 $extData = Minz_ExtensionManager::callHook('js_vars', []);

+ 5 - 1
app/views/helpers/logs_pagination.phtml

@@ -1,4 +1,5 @@
 <?php
+	/** @var FreshRSS_View $this */
 	$c = Minz_Request::controllerName();
 	$a = Minz_Request::actionName();
 	$params = $_GET;
@@ -6,7 +7,10 @@
 
 <?php if ($this->nbPage > 1) { ?>
 <ul class="pagination">
-	<?php $params[$getteur] = 1; ?>
+	<?php
+		/** @var int $getteur */
+		$params[$getteur] = 1;
+	?>
 	<li class="item pager-first">
 		<?php if ($this->currentPage > 1) { ?>
 		<a href="<?= Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)) ?>">« <?= _t('gen.pagination.first') ?></a>

+ 1 - 0
app/views/helpers/pagination.phtml

@@ -1,4 +1,5 @@
 <?php
+	/** @var FreshRSS_View $this */
 	$url_next = Minz_Request::currentRequest();
 	$url_next['params']['next'] = FreshRSS_Context::$next_id;
 	$url_next['params']['state'] = FreshRSS_Context::$state;

+ 1 - 0
app/views/importExport/export.phtml

@@ -1 +1,2 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?= $this->content ?>

+ 4 - 1
app/views/importExport/index.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post ">
 	<div class="link-back-wrapper">

+ 6 - 3
app/views/index/about.phtml

@@ -1,6 +1,9 @@
-<?php if (FreshRSS_Auth::hasAccess()) {?>
-<?php $this->partial('aside_configure'); ?>
-<?php } ?>
+<?php
+	/** @var FreshRSS_View $this */
+	if (FreshRSS_Auth::hasAccess()) {
+		$this->partial('aside_configure');
+	}
+?>
 
 <main class="post content">
 	<?php if (FreshRSS_Auth::hasAccess()) {?>

+ 1 - 0
app/views/index/global.phtml

@@ -1,4 +1,5 @@
 <?php
+	/** @var FreshRSS_View $this */
 	$this->partial('nav_menu');
 
 	$class = '';

+ 1 - 0
app/views/index/logs.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <main class="post content">
 	<div class="link-back-wrapper">
 		<a class="link-back" href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a>

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

@@ -1,4 +1,5 @@
 <?php
+/** @var FreshRSS_View $this */
 $this->partial('aside_feed');
 $this->partial('nav_menu');
 

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

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 $this->partial('aside_feed');
 $this->partial('nav_menu');

+ 1 - 0
app/views/index/rss.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?= '<?xml version="1.0" encoding="UTF-8" ?>'; ?>
 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
 	<channel>

+ 1 - 0
app/views/index/tos.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <main class="post content">
 	<?php if ($this->can_register) { ?>
 		<a href="<?= _url('auth', 'register') ?>">

+ 1 - 0
app/views/javascript/actualize.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 $feeds = array();
 foreach ($this->feeds as $feed) {

+ 1 - 0
app/views/javascript/nbUnreadsPerFeed.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 $result = array(
 	'feeds' => array(),

+ 1 - 0
app/views/javascript/nonce.phtml

@@ -1,2 +1,3 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 echo json_encode(array('salt1' => $this->salt1, 'nonce' => $this->nonce));

+ 4 - 1
app/views/stats/idle.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 4 - 1
app/views/stats/index.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 4 - 1
app/views/stats/repartition.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post ">
 	<div class="link-back-wrapper">

+ 5 - 2
app/views/subscription/add.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post drop-section">
 	<div class="link-back-wrapper">
@@ -25,7 +28,7 @@
 
 	<h2><?= _t('sub.title.add_feed') ?></h2>
 	<form id="add_rss" method="post" action="<?= _url('feed', 'add') ?>" autocomplete="off">
-		
+
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
 
 		<div class="form-group">

+ 4 - 1
app/views/subscription/bookmarklet.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

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

@@ -1,5 +1,5 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
-
 if ($this->category) {
 	$this->renderHelper('category/update');
 }

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

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 
 if (!Minz_Request::param('ajax')) {

+ 5 - 2
app/views/subscription/index.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post drop-section">
 	<div class="link-back-wrapper">
@@ -14,7 +17,7 @@
 		<p class="alert alert-warn">
 		<?= _t('sub.feed.showing.error') ?>
 	</p>
-	
+
 	<?php endif; ?>
 
 	<?php if (!$this->onlyFeedsWithError && $this->signalError){ ?>

+ 1 - 0
app/views/tag/getTagsForEntry.phtml

@@ -1,2 +1,3 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 echo json_encode($this->tags);

+ 5 - 2
app/views/tag/index.phtml

@@ -1,10 +1,13 @@
-<?php $this->partial('aside_subscription'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_subscription');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
 		<a class="link-back" href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a>
 	</div>
- 
+
 	<h1><?= _t('sub.menu.label_management') ?></h1>
 
 	<h2><?= _t('sub.title.add_label') ?></h2>

+ 8 - 2
app/views/update/apply.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
@@ -7,5 +10,8 @@
 
 	<h1><?= _t('admin.update') ?></h1>
 
-	<?php ask_info_update(); ?>
+	<?php
+		// @phpstan-ignore-next-line
+		ask_info_update();
+	?>
 </main>

+ 4 - 1
app/views/update/checkInstall.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 4 - 1
app/views/update/index.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">

+ 4 - 1
app/views/user/details.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <?php $isDefault = $this->details['is_default']; ?>
 <?php $isAdmin = $this->details['is_admin']; ?>

+ 5 - 2
app/views/user/manage.phtml

@@ -1,4 +1,7 @@
-<?php $this->partial('aside_configure'); ?>
+<?php
+	/** @var FreshRSS_View $this */
+	$this->partial('aside_configure');
+?>
 
 <main class="post">
 	<div class="link-back-wrapper">
@@ -12,7 +15,7 @@
 		<input type="hidden" name="originController" value="<?= Minz_Request::controllerName() ?>" />
 		<input type="hidden" name="originAction" value="<?= Minz_Request::actionName() ?>" />
 
-		
+
 
 		<div class="form-group">
 			<label class="group-name" for="new_user_language"><?= _t('admin.user.language') ?></label>

+ 2 - 1
app/views/user/profile.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?php
 	if (!$this->disable_aside) {
 		$this->partial('aside_configure');
@@ -103,7 +104,7 @@
 
 	<form id="crypto-form" method="post" action="<?= _url('user', 'delete') ?>">
 		<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
-		
+
 		<p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('conf.profile.delete.warn') ?></p>
 
 		<div class="form-group">

+ 1 - 0
app/views/user/validateEmail.phtml

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <main class="post">
 	<p>
 		<?= _t('user.email.validation.need_to', FreshRSS_Context::$system_conf->title) ?>

+ 1 - 0
app/views/user_mailer/email_need_validation.txt.php

@@ -1,3 +1,4 @@
+<?php /** @var FreshRSS_View $this */ ?>
 <?= _t('user.mailer.email_need_validation.welcome', $this->username) ?>
 
 <?= _t('user.mailer.email_need_validation.body', $this->site_title) ?>

+ 4 - 2
cli/_update-or-create-user.php

@@ -17,11 +17,13 @@ $params = array(
 		'max_posts_per_rss:',
 	);
 
-if (!$isUpdate) {
+if (!isset($isUpdate)) {
+	$isUpdate = false;
+} elseif (!$isUpdate) {
 	$params[] = 'no_default_feeds';	//Only for creating new users
 }
 
-$options = getopt('', $params);
+$GLOBALS['options'] = getopt('', $params);
 
 if (!validateOptions($argv, $params) || empty($options['user'])) {
 	fail('Usage: ' . basename($_SERVER['SCRIPT_FILENAME']) .

+ 2 - 2
cli/create-user.php

@@ -3,7 +3,7 @@
 $isUpdate = false;
 require(__DIR__ . '/_update-or-create-user.php');
 
-$username = $options['user'];
+$username = $GLOBALS['options']['user'];
 if (!FreshRSS_user_Controller::checkUsername($username)) {
 	fail('FreshRSS error: invalid username “' . $username .
 		'”! Must be matching ' . FreshRSS_user_Controller::USERNAME_PATTERN);
@@ -20,7 +20,7 @@ $ok = FreshRSS_user_Controller::createUser(
 	$username,
 	empty($options['mail_login']) ? '' : $options['mail_login'],
 	empty($options['password']) ? '' : $options['password'],
-	$values,
+	$GLOBALS['values'],
 	!isset($options['no_default_feeds'])
 );
 

+ 2 - 2
cli/update-user.php

@@ -3,7 +3,7 @@
 $isUpdate = true;
 require(__DIR__ . '/_update-or-create-user.php');
 
-$username = cliInitUser($options['user']);
+$username = cliInitUser($GLOBALS['options']['user']);
 
 echo 'FreshRSS updating user “', $username, "”…\n";
 
@@ -11,7 +11,7 @@ $ok = FreshRSS_user_Controller::updateUser(
 	$username,
 	empty($options['mail_login']) ? null : $options['mail_login'],
 	empty($options['password']) ? '' : $options['password'],
-	$values);
+	$GLOBALS['values']);
 
 if (!$ok) {
 	fail('FreshRSS could not update user!');

+ 1 - 1
cli/user-info.php

@@ -26,10 +26,10 @@ if (empty($options['user'])) {
 sort($users);
 
 $formatJson = isset($options['json']);
+$jsonOutput = [];
 if ($formatJson) {
 	unset($options['header']);
 	unset($options['h']);
-	$jsonOutput = [];
 }
 
 if (array_key_exists('header', $options)) {

+ 4 - 1
composer.json

@@ -20,6 +20,7 @@
         "php": ">=7.0.0"
     },
     "require-dev": {
+        "phpstan/phpstan": "^1.2.0",
         "phpunit/phpunit": "^9",
         "squizlabs/php_codesniffer": "^3.6"
     },
@@ -28,13 +29,15 @@
         "phtml-lint": "find . -type d -name 'vendor' -prune -o -name '*.phtml' -print0 | xargs -0 -n1 -P4 php -l 1>/dev/null",
         "phpcs": "phpcs . -s",
         "phpcbf": "phpcbf . -p -s",
+        "phpstan": "phpstan analyse .",
         "phpunit": "phpunit --bootstrap ./tests/bootstrap.php --verbose ./tests",
         "translations": "cli/manipulate.translation.php -a format",
         "test": [
             "@php-lint",
             "@phtml-lint",
             "@phpunit",
-            "@phpcs"
+            "@phpcs",
+            "@phpstan"
         ],
         "fix": [
             "@translations",

+ 165 - 100
composer.lock

@@ -4,8 +4,9 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "7bbc6a80041cfcb94173f7f27e399ecb",
-    "packages": [
+    "content-hash": "d18f8d175a9bb370f3ecc28362ef282a",
+    "packages": [],
+    "packages-dev": [
         {
             "name": "doctrine/instantiator",
             "version": "1.4.0",
@@ -135,16 +136,16 @@
         },
         {
             "name": "nikic/php-parser",
-            "version": "v4.13.0",
+            "version": "v4.13.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "50953a2691a922aa1769461637869a0a2faa3f53"
+                "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53",
-                "reference": "50953a2691a922aa1769461637869a0a2faa3f53",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
+                "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
                 "shasum": ""
             },
             "require": {
@@ -185,9 +186,9 @@
             ],
             "support": {
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0"
+                "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
             },
-            "time": "2021-09-20T12:20:58+00:00"
+            "time": "2021-11-30T19:35:32+00:00"
         },
         {
             "name": "phar-io/manifest",
@@ -462,16 +463,16 @@
         },
         {
             "name": "phpspec/prophecy",
-            "version": "1.14.0",
+            "version": "v1.15.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e"
+                "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
-                "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
+                "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
                 "shasum": ""
             },
             "require": {
@@ -523,29 +524,93 @@
             ],
             "support": {
                 "issues": "https://github.com/phpspec/prophecy/issues",
-                "source": "https://github.com/phpspec/prophecy/tree/1.14.0"
+                "source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
+            },
+            "time": "2021-12-08T12:19:24+00:00"
+        },
+        {
+            "name": "phpstan/phpstan",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpstan.git",
+                "reference": "ffc5aee6019eeae4ea618d97dd290ab95e77be59"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ffc5aee6019eeae4ea618d97dd290ab95e77be59",
+                "reference": "ffc5aee6019eeae4ea618d97dd290ab95e77be59",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1|^8.0"
+            },
+            "conflict": {
+                "phpstan/phpstan-shim": "*"
+            },
+            "bin": [
+                "phpstan",
+                "phpstan.phar"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ]
             },
-            "time": "2021-09-10T09:02:12+00:00"
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPStan - PHP Static Analysis Tool",
+            "support": {
+                "issues": "https://github.com/phpstan/phpstan/issues",
+                "source": "https://github.com/phpstan/phpstan/tree/1.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ondrejmirtes",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/phpstan",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/phpstan",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-12-29T17:03:31+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "9.2.7",
+            "version": "9.2.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
+                "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
-                "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687",
+                "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-libxml": "*",
                 "ext-xmlwriter": "*",
-                "nikic/php-parser": "^4.12.0",
+                "nikic/php-parser": "^4.13.0",
                 "php": ">=7.3",
                 "phpunit/php-file-iterator": "^3.0.3",
                 "phpunit/php-text-template": "^2.0.2",
@@ -594,7 +659,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10"
             },
             "funding": [
                 {
@@ -602,20 +667,20 @@
                     "type": "github"
                 }
             ],
-            "time": "2021-09-17T05:39:03+00:00"
+            "time": "2021-12-05T09:12:13+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
-            "version": "3.0.5",
+            "version": "3.0.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8"
+                "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8",
-                "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+                "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
                 "shasum": ""
             },
             "require": {
@@ -654,7 +719,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
-                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
+                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
             },
             "funding": [
                 {
@@ -662,7 +727,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T05:57:25+00:00"
+            "time": "2021-12-02T12:48:52+00:00"
         },
         {
             "name": "phpunit/php-invoker",
@@ -847,16 +912,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.5.10",
+            "version": "9.5.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
+                "reference": "2406855036db1102126125537adb1406f7242fdd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
-                "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2406855036db1102126125537adb1406f7242fdd",
+                "reference": "2406855036db1102126125537adb1406f7242fdd",
                 "shasum": ""
             },
             "require": {
@@ -934,11 +999,11 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.11"
             },
             "funding": [
                 {
-                    "url": "https://phpunit.de/donate.html",
+                    "url": "https://phpunit.de/sponsors.html",
                     "type": "custom"
                 },
                 {
@@ -946,7 +1011,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2021-09-25T07:38:51+00:00"
+            "time": "2021-12-25T07:07:57+00:00"
         },
         {
             "name": "sebastian/cli-parser",
@@ -1377,16 +1442,16 @@
         },
         {
             "name": "sebastian/exporter",
-            "version": "4.0.3",
+            "version": "4.0.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65"
+                "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65",
-                "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+                "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
                 "shasum": ""
             },
             "require": {
@@ -1435,14 +1500,14 @@
                 }
             ],
             "description": "Provides the functionality to export PHP variables for visualization",
-            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "homepage": "https://www.github.com/sebastianbergmann/exporter",
             "keywords": [
                 "export",
                 "exporter"
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/exporter/issues",
-                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
+                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
             },
             "funding": [
                 {
@@ -1450,7 +1515,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T05:24:23+00:00"
+            "time": "2021-11-11T14:18:36+00:00"
         },
         {
             "name": "sebastian/global-state",
@@ -1912,6 +1977,62 @@
             ],
             "time": "2020-09-28T06:39:44+00:00"
         },
+        {
+            "name": "squizlabs/php_codesniffer",
+            "version": "3.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+                "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
+                "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
+                "shasum": ""
+            },
+            "require": {
+                "ext-simplexml": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+            },
+            "bin": [
+                "bin/phpcs",
+                "bin/phpcbf"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Sherwood",
+                    "role": "lead"
+                }
+            ],
+            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+            "keywords": [
+                "phpcs",
+                "standards"
+            ],
+            "support": {
+                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+            },
+            "time": "2021-12-12T21:44:58+00:00"
+        },
         {
             "name": "symfony/polyfill-ctype",
             "version": "v1.23.0",
@@ -2100,70 +2221,14 @@
             "time": "2021-03-09T10:59:23+00:00"
         }
     ],
-    "packages-dev": [
-        {
-            "name": "squizlabs/php_codesniffer",
-            "version": "3.6.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f268ca40d54617c6e06757f83f699775c9b3ff2e",
-                "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e",
-                "shasum": ""
-            },
-            "require": {
-                "ext-simplexml": "*",
-                "ext-tokenizer": "*",
-                "ext-xmlwriter": "*",
-                "php": ">=5.4.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
-            },
-            "bin": [
-                "bin/phpcs",
-                "bin/phpcbf"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.x-dev"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Greg Sherwood",
-                    "role": "lead"
-                }
-            ],
-            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
-            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
-            "keywords": [
-                "phpcs",
-                "standards"
-            ],
-            "support": {
-                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
-                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
-                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
-            },
-            "time": "2021-10-11T04:00:11+00:00"
-        }
-    ],
     "aliases": [],
     "minimum-stability": "stable",
     "stability-flags": [],
     "prefer-stable": false,
     "prefer-lowest": false,
-    "platform": [],
+    "platform": {
+        "php": ">=7.0.0"
+    },
     "platform-dev": [],
     "plugin-api-version": "2.0.0"
 }

+ 8 - 1
lib/Minz/ActionController.php

@@ -13,11 +13,18 @@ class Minz_ActionController {
 		'default-src' => "'self'",
 	);
 
+	// Gives the possibility to override the default View type.
+	public static $viewType = 'Minz_View';
+
 	/**
 	 * Constructeur
 	 */
 	public function __construct () {
-		$this->view = new Minz_View();
+		if (class_exists(self::$viewType)) {
+			$this->view = new self::$viewType();
+		} else {
+			$this->view = new Minz_View();
+		}
 		$view_path = Minz_Request::controllerName() . '/' . Minz_Request::actionName() . '.phtml';
 		$this->view->_path($view_path);
 		$this->view->attributeParams ();

+ 5 - 5
lib/Minz/Configuration.php

@@ -87,15 +87,15 @@ class Minz_Configuration {
 	private $extensions_enabled = [];
 
 	public function removeExtension($ext_name) {
-		unset(self::$extensions_enabled[$ext_name]);
-		$legacyKey = array_search($ext_name, self::$extensions_enabled, true);
+		unset($this->extensions_enabled[$ext_name]);
+		$legacyKey = array_search($ext_name, $this->extensions_enabled, true);
 		if ($legacyKey !== false) {	//Legacy format FreshRSS < 1.11.1
-			unset(self::$extensions_enabled[$legacyKey]);
+			unset($this->extensions_enabled[$legacyKey]);
 		}
 	}
 	public function addExtension($ext_name) {
-		if (!isset(self::$extensions_enabled[$ext_name])) {
-			self::$extensions_enabled[$ext_name] = true;
+		if (!isset($this->extensions_enabled[$ext_name])) {
+			$this->extensions_enabled[$ext_name] = true;
 		}
 	}
 

+ 1 - 1
lib/Minz/ControllerNotExistException.php

@@ -1,6 +1,6 @@
 <?php
 class Minz_ControllerNotExistException extends Minz_Exception {
-	public function __construct ($controller_name, $code = self::ERROR) {
+	public function __construct ($code = self::ERROR) {
 		$message = 'Controller not found!';
 		parent::__construct ($message, $code);
 	}

+ 0 - 1
lib/Minz/Dispatcher.php

@@ -83,7 +83,6 @@ class Minz_Dispatcher {
 
 		if (!class_exists ($controller_name)) {
 			throw new Minz_ControllerNotExistException (
-				$controller_name,
 				Minz_Exception::ERROR
 			);
 		}

+ 4 - 0
lib/Minz/Mailer.php

@@ -32,6 +32,10 @@ class Minz_Mailer {
 	 */
 	protected $view;
 
+	private $mailer;
+	private $smtp_config;
+	private $debug_level;
+
 	/**
 	 * Constructor.
 	 */

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません