Просмотр исходного кода

Use native PHP #[Deprecated] (#8325)

https://php.watch/versions/8.4/Deprecated
And enfore it with PHPUnit + PHPStan.
Especially useful for extensions.
Alexandre Alapetite 3 месяцев назад
Родитель
Сommit
00cd5df294

+ 2 - 4
app/Controllers/indexController.php

@@ -227,8 +227,8 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 
 	/**
 	 * This action displays the RSS feed of FreshRSS.
-	 * @deprecated See user query RSS sharing instead
 	 */
+	#[Deprecated('See user query RSS sharing instead')]
 	public function rssAction(): void {
 		$allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous;
 
@@ -262,9 +262,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
 		header('Content-Type: application/rss+xml; charset=utf-8');
 	}
 
-	/**
-	 * @deprecated See user query OPML sharing instead
-	 */
+	#[Deprecated('See user query OPML sharing instead')]
 	public function opmlAction(): void {
 		$allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous;
 

+ 9 - 0
app/Models/Context.php

@@ -57,11 +57,13 @@ final class FreshRSS_Context {
 	/**
 	 * @access private
 	 * @deprecated Will be made `private`; use `FreshRSS_Context::systemConf()` instead.
+	 * @internal
 	 */
 	public static ?FreshRSS_SystemConfiguration $system_conf = null;
 	/**
 	 * @access private
 	 * @deprecated Will be made `private`; use `FreshRSS_Context::userConf()` instead.
+	 * @internal
 	 */
 	public static ?FreshRSS_UserConfiguration $user_conf = null;
 
@@ -186,6 +188,13 @@ final class FreshRSS_Context {
 		FreshRSS_Context::$user_conf = null;
 	}
 
+	/**
+	 * @internal
+	 */
+	public static function setUserConf(?FreshRSS_UserConfiguration $user_conf): void {
+		FreshRSS_Context::$user_conf = $user_conf;
+	}
+
 	/** @return array<int,FreshRSS_Category> where the key is the category ID */
 	public static function categories(): array {
 		if (empty(self::$categories)) {

+ 4 - 2
app/Models/Entry.php

@@ -160,7 +160,8 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 		return $title;
 	}
-	/** @deprecated */
+
+	#[Deprecated('Use authors() instead')]
 	public function author(): string {
 		return $this->authors(true);
 	}
@@ -540,7 +541,8 @@ HTML;
 		$this->hash = '';
 		$this->title = trim($value);
 	}
-	/** @deprecated */
+
+	#[Deprecated('Use _authors() instead')]
 	public function _author(string $value): void {
 		$this->_authors($value);
 	}

+ 1 - 1
app/Models/Feed.php

@@ -40,7 +40,7 @@ class FreshRSS_Feed extends Minz_Model {
 	public const PRIORITY_CATEGORY = 0;
 	public const PRIORITY_FEED = -5;
 	public const PRIORITY_HIDDEN = -10;
-	/** @deprecated use PRIORITY_HIDDEN instead */
+	#[Deprecated('Use PRIORITY_HIDDEN instead')]
 	public const PRIORITY_ARCHIVED = -10;
 
 	public const TTL_DEFAULT = 0;

+ 2 - 2
app/Models/SimplePieCustom.php

@@ -13,8 +13,8 @@ final class FreshRSS_SimplePieCustom extends \SimplePie\SimplePie
 		$limits = FreshRSS_Context::systemConf()->limits;
 		$this->get_registry()->register(\SimplePie\File::class, FreshRSS_SimplePieFetch::class);
 		$this->set_useragent(FRESHRSS_USERAGENT);
-		$this->set_cache_name_function('sha1');
-		$this->set_cache_location(CACHE_PATH);
+		$this->set_cache_name_function('sha1');	// @phpstan-ignore method.deprecated
+		$this->set_cache_location(CACHE_PATH);	// @phpstan-ignore method.deprecated
 		$this->set_cache_duration($limits['cache_duration'], $limits['cache_duration_min'], $limits['cache_duration_max']);
 		$this->enable_order_by_date(false);
 

+ 2 - 1
composer.json

@@ -58,6 +58,7 @@
 		"ext-tokenizer": "*",
 		"ext-xmlwriter": "*",
 		"phpstan/phpstan": "^2.1",
+		"phpstan/phpstan-deprecation-rules": "^2.0",
 		"phpstan/phpstan-phpunit": "^2.0",
 		"phpstan/phpstan-strict-rules": "^2.0",
 		"phpunit/phpunit": "^10",
@@ -70,7 +71,7 @@
 		"phpcbf": "phpcbf . -p -s",
 		"phpstan": "phpstan analyse --memory-limit 512M .",
 		"phpstan-next": "phpstan analyse --memory-limit 512M -c phpstan-next.neon .",
-		"phpunit": "phpunit --bootstrap ./tests/bootstrap.php --display-notices --display-phpunit-deprecations ./tests",
+		"phpunit": "phpunit --bootstrap ./tests/bootstrap.php --display-notices --display-deprecations --display-phpunit-deprecations ./tests",
 		"translations": "cli/manipulate.translation.php --action format && cli/check.translation.php --generate-readme",
 		"test": [
 			"@php-lint",

+ 76 - 29
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "3a4fbd6ff5c1cb410b629030fe0571e7",
+    "content-hash": "a9fa7dc52d7ae1c05b7c889cb59881db",
     "packages": [],
     "packages-dev": [
         {
@@ -69,16 +69,16 @@
         },
         {
             "name": "nikic/php-parser",
-            "version": "v5.6.2",
+            "version": "v5.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "3a454ca033b9e06b63282ce19562e892747449bb"
+                "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
-                "reference": "3a454ca033b9e06b63282ce19562e892747449bb",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+                "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
                 "shasum": ""
             },
             "require": {
@@ -121,9 +121,9 @@
             ],
             "support": {
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
+                "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
             },
-            "time": "2025-10-21T19:32:17+00:00"
+            "time": "2025-12-06T11:56:16+00:00"
         },
         {
             "name": "phar-io/manifest",
@@ -245,11 +245,11 @@
         },
         {
             "name": "phpstan/phpstan",
-            "version": "2.1.32",
+            "version": "2.1.33",
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e126cad1e30a99b137b8ed75a85a676450ebb227",
-                "reference": "e126cad1e30a99b137b8ed75a85a676450ebb227",
+                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
+                "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
                 "shasum": ""
             },
             "require": {
@@ -294,20 +294,67 @@
                     "type": "github"
                 }
             ],
-            "time": "2025-11-11T15:18:17+00:00"
+            "time": "2025-12-05T10:24:31+00:00"
+        },
+        {
+            "name": "phpstan/phpstan-deprecation-rules",
+            "version": "2.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
+                "reference": "468e02c9176891cc901143da118f09dc9505fc2f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f",
+                "reference": "468e02c9176891cc901143da118f09dc9505fc2f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.4 || ^8.0",
+                "phpstan/phpstan": "^2.1.15"
+            },
+            "require-dev": {
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^9.6"
+            },
+            "type": "phpstan-extension",
+            "extra": {
+                "phpstan": {
+                    "includes": [
+                        "rules.neon"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
+            "support": {
+                "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
+                "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3"
+            },
+            "time": "2025-05-14T10:56:57+00:00"
         },
         {
             "name": "phpstan/phpstan-phpunit",
-            "version": "2.0.8",
+            "version": "2.0.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpstan-phpunit.git",
-                "reference": "2fe9fbeceaf76dd1ebaa7bbbb25e2fb5e59db2fe"
+                "reference": "8d61a5854e7497d95bc85188e13537e99bd7aae7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/2fe9fbeceaf76dd1ebaa7bbbb25e2fb5e59db2fe",
-                "reference": "2fe9fbeceaf76dd1ebaa7bbbb25e2fb5e59db2fe",
+                "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/8d61a5854e7497d95bc85188e13537e99bd7aae7",
+                "reference": "8d61a5854e7497d95bc85188e13537e99bd7aae7",
                 "shasum": ""
             },
             "require": {
@@ -345,9 +392,9 @@
             "description": "PHPUnit extensions and rules for PHPStan",
             "support": {
                 "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
-                "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.8"
+                "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.10"
             },
-            "time": "2025-11-11T07:55:22+00:00"
+            "time": "2025-12-06T11:15:39+00:00"
         },
         {
             "name": "phpstan/phpstan-strict-rules",
@@ -720,16 +767,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "10.5.58",
+            "version": "10.5.60",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca"
+                "reference": "f2e26f52f80ef77832e359205f216eeac00e320c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e24fb46da450d8e6a5788670513c1af1424f16ca",
-                "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f2e26f52f80ef77832e359205f216eeac00e320c",
+                "reference": "f2e26f52f80ef77832e359205f216eeac00e320c",
                 "shasum": ""
             },
             "require": {
@@ -801,7 +848,7 @@
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.58"
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.60"
             },
             "funding": [
                 {
@@ -825,7 +872,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2025-09-28T12:04:46+00:00"
+            "time": "2025-12-06T07:50:42+00:00"
         },
         {
             "name": "sebastian/cli-parser",
@@ -1861,16 +1908,16 @@
         },
         {
             "name": "theseer/tokenizer",
-            "version": "1.2.3",
+            "version": "1.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/theseer/tokenizer.git",
-                "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+                "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
-                "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+                "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
                 "shasum": ""
             },
             "require": {
@@ -1899,7 +1946,7 @@
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
             "support": {
                 "issues": "https://github.com/theseer/tokenizer/issues",
-                "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+                "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
             },
             "funding": [
                 {
@@ -1907,7 +1954,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2024-03-03T12:36:25+00:00"
+            "time": "2025-11-17T20:03:58+00:00"
         }
     ],
     "aliases": [],

+ 4 - 2
lib/Minz/ActionController.php

@@ -27,6 +27,8 @@ abstract class Minz_ActionController {
 	 * Gives the possibility to override the default view model type.
 	 * @var class-string
 	 * @deprecated Use constructor with view type instead
+	 * @access private
+	 * @internal
 	 */
 	public static string $defaultViewType = Minz_View::class;
 
@@ -43,8 +45,8 @@ abstract class Minz_ActionController {
 				$view = null;
 			}
 		}
-		if ($view === null && class_exists(self::$defaultViewType)) {
-			$view = new self::$defaultViewType();
+		if ($view === null && class_exists(self::$defaultViewType)) {	/// @phpstan-ignore staticProperty.deprecated
+			$view = new self::$defaultViewType();	// @phpstan-ignore staticProperty.deprecated
 			if (!($view instanceof Minz_View)) {
 				$view = null;
 			}

+ 25 - 17
lib/Minz/Configuration.php

@@ -26,7 +26,7 @@ class Minz_Configuration {
 	 * @param string $namespace the name of the current configuration
 	 * @param string $config_filename the filename of the configuration
 	 * @param string $default_filename a filename containing default values for the configuration
-	 * @param Minz_ConfigurationSetterInterface $configuration_setter an optional helper to set values in configuration
+	 * @param Minz_ConfigurationSetterInterface $configuration_setter an optional helper to set values in configuration @deprecated
 	 * @throws Minz_FileNotExistException
 	 */
 	public static function register(string $namespace, string $config_filename, ?string $default_filename = null,
@@ -102,7 +102,7 @@ class Minz_Configuration {
 	 * @param string $namespace the name of the current configuration.
 	 * @param string $config_filename the file containing configuration values.
 	 * @param string $default_filename the file containing default values, null by default.
-	 * @param Minz_ConfigurationSetterInterface $configuration_setter an optional helper to set values in configuration
+	 * @param Minz_ConfigurationSetterInterface $configuration_setter an optional helper to set values in configuration @deprecated
 	 * @throws Minz_FileNotExistException
 	 */
 	final private function __construct(string $namespace, string $config_filename, ?string $default_filename = null,
@@ -138,6 +138,7 @@ class Minz_Configuration {
 		}
 	}
 
+	#[Deprecated]
 	public function configurationSetter(): ?Minz_ConfigurationSetterInterface {
 		return $this->configuration_setter;
 	}
@@ -155,13 +156,12 @@ class Minz_Configuration {
 	 * @param string $key the name of the param.
 	 * @param mixed $default default value to return if key does not exist.
 	 * @return array|mixed value corresponding to the key.
-	 * @access private
-	 * @deprecated Use `attribute*()` methods instead.
 	 */
+	#[Deprecated('Use `attribute*()` methods instead.')]
 	public function param(string $key, mixed $default = null): mixed {
 		if (isset($this->data[$key])) {
 			return $this->data[$key];
-		} elseif (!is_null($default)) {
+		} elseif ($default !== null) {
 			return $default;
 		} else {
 			Minz_Log::warning($key . ' does not exist in configuration');
@@ -170,13 +170,14 @@ class Minz_Configuration {
 	}
 
 	/**
-	 * A wrapper for param().
 	 * @return array|mixed
-	 * @access private
-	 * @deprecated
 	 */
 	public function __get(string $key): mixed {
-		return $this->param($key);
+		if (isset($this->data[$key])) {
+			return $this->data[$key];
+		}
+		Minz_Log::warning($key . ' does not exist in configuration');
+		return null;
 	}
 
 	/**
@@ -184,13 +185,12 @@ class Minz_Configuration {
 	 *
 	 * @param string $key the param name to set.
 	 * @param mixed $value the value to set. If null, the key is removed from the configuration.
-	 * @access private
-	 * @deprecated Use `_attribute()` instead.
 	 */
+	#[Deprecated('Use `_attribute()` instead.')]
 	public function _param(string $key, mixed $value = null): void {
 		if ($this->configuration_setter !== null && $this->configuration_setter->support($key)) {
 			$this->configuration_setter->handle($this->data, $key, $value);
-		} elseif (isset($this->data[$key]) && is_null($value)) {
+		} elseif (isset($this->data[$key]) && $value === null) {
 			unset($this->data[$key]);
 		} elseif ($value !== null) {
 			$this->data[$key] = $value;
@@ -198,12 +198,16 @@ class Minz_Configuration {
 	}
 
 	/**
-	 * A wrapper for _param().
-	 * @access private
-	 * @deprecated
+	 * {@see Minz_Configuration::_attribute()} instead.
+	 * @param string $key the param name to set.
+	 * @param mixed $value the value to set. If null, the key is removed.
 	 */
 	public function __set(string $key, mixed $value): void {
-		$this->_param($key, $value);
+		if ($value === null) {
+			unset($this->data[$key]);
+		} else {
+			$this->data[$key] = $value;
+		}
 	}
 
 	/**
@@ -266,6 +270,10 @@ class Minz_Configuration {
 	 * @param array<string,mixed>|mixed|null $value Value, not HTML-encoded
 	 */
 	public function _attribute(string $key, $value = null): void {
-		self::_param($key, $value);
+		if (isset($this->data[$key]) && $value === null) {
+			unset($this->data[$key]);
+		} elseif ($value !== null) {
+			$this->data[$key] = $value;
+		}
 	}
 }

+ 1 - 1
lib/Minz/Request.php

@@ -42,8 +42,8 @@ class Minz_Request {
 	 * @param mixed $default default value, if no parameter is given
 	 * @param bool $specialchars `true` to return special characters, `false` (default) to XML-encode them
 	 * @return mixed value of the parameter
-	 * @deprecated use typed versions instead
 	 */
+	#[Deprecated('Use typed versions instead')]
 	public static function param(string $key, mixed $default = false, bool $specialchars = false): mixed {
 		if (isset(self::$params[$key])) {
 			$p = self::$params[$key];

+ 1 - 1
lib/Minz/Session.php

@@ -61,8 +61,8 @@ class Minz_Session {
 	 * @param string $p the parameter to retrieve
 	 * @param mixed|false $default the default value if the parameter doesn’t exist
 	 * @return mixed|false the value of the session variable, false if doesn’t exist
-	 * @deprecated Use typed versions instead
 	 */
+	#[Deprecated('Use typed versions instead')]
 	public static function param(string $p, $default = false): mixed {
 		return $_SESSION[$p] ?? $default;
 	}

+ 3 - 2
lib/Minz/View.php

@@ -39,8 +39,9 @@ class Minz_View {
 	}
 
 	/**
-	 * @deprecated Change the view file based on controller and action.
+	 * Change the view file based on controller and action.
 	 */
+	#[Deprecated('Use Minz_View::_path() instead.')]
 	public function change_view(string $controller_name, string $action_name): void {
 		Minz_Log::warning('Minz_View::change_view is deprecated, it will be removed in a future version. Please use Minz_View::_path instead.');
 		$this->_path($controller_name . '/' . $action_name . '.phtml');
@@ -170,9 +171,9 @@ class Minz_View {
 
 	/**
 	 * Choose if we want to use the layout or not.
-	 * @deprecated Please use the `_layout` function instead.
 	 * @param bool $use true if we want to use the layout, false else
 	 */
+	#[Deprecated('Use Minz_View::_layout() instead.')]
 	public function _useLayout(bool $use): void {
 		Minz_Log::warning('Minz_View::_useLayout is deprecated, it will be removed in a future version. Please use Minz_View::_layout instead.');
 		if ($use) {

+ 2 - 6
lib/lib_rss.php

@@ -307,16 +307,12 @@ function invalidateHttpCache(string $username = ''): bool {
 	return FreshRSS_UserDAO::ctouch($username);
 }
 
-/**
- * @deprecated Use {@see Minz_Request::connectionRemoteAddress()} instead.
- */
+#[Deprecated('Use Minz_Request::connectionRemoteAddress() instead.')]
 function connectionRemoteAddress(): string {
 	return Minz_Request::connectionRemoteAddress();
 }
 
-/**
- * @deprecated Use {@see FreshRSS_http_Util::checkTrustedIP()} instead.
- */
+#[Deprecated('Use FreshRSS_http_Util::checkTrustedIP() instead.')]
 function checkTrustedIP(): bool {
 	return FreshRSS_http_Util::checkTrustedIP();
 }

+ 4 - 0
phpstan.dist.neon

@@ -57,7 +57,11 @@ parameters:
 			- 'Minz_Exception'
 	ignoreErrors:
 		- '#Only booleans are allowed in (a negated boolean|a ternary operator condition|an elseif condition|an if condition|&&|\|\|), (bool|false|int(<[0-9, max]+>)?|true|null|\|)+ given.*#'
+		-
+			message: '#Access to deprecated#'
+			path: app/Models/Context.php
 includes:
+	- vendor/phpstan/phpstan-deprecation-rules/rules.neon
 	- vendor/phpstan/phpstan-phpunit/extension.neon
 	- vendor/phpstan/phpstan-phpunit/rules.neon
 	- vendor/phpstan/phpstan-strict-rules/rules.neon

+ 2 - 2
tests/app/Models/SearchTest.php

@@ -251,7 +251,7 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
 		$previousUserConf = FreshRSS_Context::hasUserConf() ? FreshRSS_Context::userConf() : null;
 		$newUserConf = $previousUserConf instanceof FreshRSS_UserConfiguration ? clone $previousUserConf : clone FreshRSS_UserConfiguration::default();
 		$newUserConf->queries = $queries;
-		FreshRSS_Context::$user_conf = $newUserConf;
+		FreshRSS_Context::setUserConf($newUserConf);
 
 		try {
 			$search = new FreshRSS_BooleanSearch($input);
@@ -259,7 +259,7 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
 			self::assertSame($expectedResult[0], trim($actualSql));
 			self::assertSame($expectedResult[1], $actualValues);
 		} finally {
-			FreshRSS_Context::$user_conf = $previousUserConf;
+			FreshRSS_Context::setUserConf($previousUserConf);
 		}
 	}