diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9930a50..7fa29e1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -119,4 +119,26 @@ jobs:
run: composer install --prefer-dist --no-progress --no-suggest
- name: Run PHPStan
- run: bin/phpstan analyse --error-format=github
+ run: bin/phpstan analyse --memory-limit=512M --error-format=github
+
+ psalm:
+ name: Static Analysis (Psalm)
+ runs-on: ubuntu-latest
+ continue-on-error: true
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+ extensions: mbstring, intl
+ coverage: none
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: Run Psalm
+ run: bin/psalm --memory-limit=512M --output-format=github
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
index 53d3598..c778e4d 100644
--- a/.scrutinizer.yml
+++ b/.scrutinizer.yml
@@ -14,6 +14,11 @@ checks:
build:
image: default-jammy
+ environment:
+ php: 8.2
+ dependencies:
+ override:
+ - composer install --no-interaction --no-scripts --ignore-platform-reqs
nodes:
analysis:
tests:
diff --git a/ROADMAP.md b/ROADMAP.md
index 32fdc16..9f71d56 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -88,7 +88,7 @@ Not tied to a specific release; picked up as time allows.
**Static analysis:**
- [x] PHPStan level 6 → 8 — tighter generics and inference; required four small nullable-return guards (`idn_to_ascii`, `mb_split`, `file_get_contents`) and one local docblock shape on `parseMultiple()`.
-- [ ] Add Psalm alongside PHPStan for cross-tool coverage; keep both green.
+- [x] Psalm alongside PHPStan — level 3 with baseline (66 entries, all false positives or duplicates of PHPStan findings). Found no genuinely new bugs vs PHPStan level 8; serves as a cross-check for future regressions. `composer psalm`.
**Performance:**
- [x] PhpBench suite — `benchmarks/ParseBench.php` covers single ASCII, name-addr, UTF-8 local-part, IDN, obs-route, 10-address comma batch, 100-address `parseStream` batch, invalid inputs, and comment extraction. Run with `composer bench`.
diff --git a/composer.json b/composer.json
index 68d035d..58b7380 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,8 @@
"phpunit/phpunit": "^9.6",
"symfony/yaml": "^6.4|^7.2",
"infection/infection": "^0.29.8",
- "phpbench/phpbench": "^1.4"
+ "phpbench/phpbench": "^1.4",
+ "vimeo/psalm": "^6.0"
},
"require": {
"php": "^8.1",
@@ -59,6 +60,7 @@
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"stan": "phpstan analyse --memory-limit=512M",
+ "psalm": "psalm --memory-limit=512M",
"infect": "XDEBUG_MODE=coverage infection --threads=max",
"bench": "phpbench run --report=default",
"ci": [
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
new file mode 100644
index 0000000..7c3ea55
--- /dev/null
+++ b/psalm-baseline.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 0000000..9ca84db
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+