diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
index ff82dca..b2660c2 100644
--- a/.github/workflows/static.yml
+++ b/.github/workflows/static.yml
@@ -4,9 +4,8 @@ on:
pull_request:
paths: &paths
- 'src/**'
- - 'config/**'
- '.github/workflows/static.yml'
- - 'psalm*.xml'
+ - 'phpstan.neon'
- 'composer.json'
push:
branches: ['master']
@@ -20,10 +19,10 @@ concurrency:
cancel-in-progress: true
jobs:
- psalm:
- uses: yiisoft/actions/.github/workflows/psalm.yml@master
+ phpstan:
+ uses: yiisoft/actions/.github/workflows/phpstan.yml@master
with:
os: >-
['ubuntu-latest']
php: >-
- ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29b052b..2877323 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## 1.1.1 under development
- Enh #61: Explicitly import constants in "use" section (@mspirkov)
+- Chg #70: Replace Psalm with PHPStan (@vjik)
## 1.1.0 November 21, 2025
diff --git a/README.md b/README.md
index 21134a1..706d860 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,6 @@
[](https://codecov.io/gh/yiisoft/json)
[](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/json/master)
[](https://github.com/yiisoft/json/actions/workflows/static.yml?query=branch%3Amaster)
-[](https://shepherd.dev/github/yiisoft/json)
The package provides methods to encode and decode JSON.
diff --git a/composer.json b/composer.json
index ae5e237..eece5cc 100644
--- a/composer.json
+++ b/composer.json
@@ -33,6 +33,7 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"friendsofphp/php-cs-fixer": "^3.93",
+ "phpstan/phpstan": "^2.3",
"phpunit/phpunit": "^9.6.22",
"rector/rector": "^2.8.1",
"shipmonk/composer-dependency-analyser": "^1.8",
@@ -67,6 +68,7 @@
"scripts": {
"cs-fix": "php-cs-fixer fix",
"dependency-analyser": "composer-dependency-analyser",
+ "phpstan": "phpstan",
"rector": "rector",
"test": "phpunit --testdox --no-interaction",
"test-watch": "phpunit-watcher watch"
diff --git a/docs/internals.md b/docs/internals.md
index b77907c..4ae9aeb 100644
--- a/docs/internals.md
+++ b/docs/internals.md
@@ -19,10 +19,10 @@ The package tests are checked with [Infection](https://infection.github.io/) mut
## Static analysis
-The code is statically analyzed with [Psalm](https://psalm.dev/). To run static analysis:
+The code is statically analyzed with [PHPStan](https://phpstan.org/). To run static analysis:
```shell
-./vendor/bin/psalm
+./vendor/bin/phpstan
```
## Code style
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..5287436
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,11 @@
+parameters:
+ level: max
+ paths:
+ - src
+ ignoreErrors:
+ # `UnitEnum` is not available in PHP 8.0 and earlier
+ -
+ identifier: class.notFound
+ message: '#^Class UnitEnum not found\.$#'
+ path: src/Json.php
+ reportUnmatched: false
diff --git a/psalm.xml b/psalm.xml
deleted file mode 100644
index 590dd10..0000000
--- a/psalm.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/Json.php b/src/Json.php
index 86de110..c54db88 100644
--- a/src/Json.php
+++ b/src/Json.php
@@ -44,9 +44,7 @@ final class Json
* @param int $options The encoding options. For more details please refer to
* {@see https://www.php.net/manual/en/function.json-encode.php}.
* Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR`.
- * @param int $depth The maximum depth.
- *
- * @psalm-param int<1, 2147483647> $depth
+ * @param int<1, 2147483647> $depth The maximum depth.
*
* @throws JsonException if there is any encoding error.
*
@@ -64,7 +62,7 @@ public static function encode(
}
/**
- * @var string We use flag `JSON_THROW_ON_ERROR`, so `json_encode` never returns `false`.
+ * @var non-empty-string We use flag `JSON_THROW_ON_ERROR`, so `json_encode` never returns `false`.
*/
return json_encode($value, JSON_THROW_ON_ERROR | $options, $depth);
}
@@ -94,11 +92,9 @@ public static function htmlEncode($value): string
*
* @param string $json The JSON string to be decoded.
* @param bool $asArray Whether to return objects in terms of associative arrays.
- * @param int $depth The recursion depth.
+ * @param int<1, 2147483647> $depth The recursion depth.
* @param int $options The decode options.
*
- * @psalm-param int<1, 2147483647> $depth
- *
* @throws JsonException If there is any decoding error.
*
* @return mixed The PHP data.
@@ -118,9 +114,9 @@ public static function decode(
/**
* Pre-processes the array before sending it to `json_encode()`.
*
- * @param array $data The array to be processed.
+ * @param array $data The array to be processed.
*
- * @return array The processed array.
+ * @return array The processed array.
*/
private static function processArray(array $data): array
{
@@ -158,7 +154,6 @@ private static function processObject(object $data)
return $data;
}
- /** @psalm-suppress UndefinedClass Required for PHP 8.0 and earlier */
if ($data instanceof DateTimeInterface
|| $data instanceof UnitEnum
) {
diff --git a/tools/psalm/composer.json b/tools/psalm/composer.json
deleted file mode 100644
index 107bf54..0000000
--- a/tools/psalm/composer.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "require-dev": {
- "vimeo/psalm": "^4.30 || ^5.26.1 || ^6.5"
- }
-}