PHPackages                             alexandr-penkin/psalm-fixer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. alexandr-penkin/psalm-fixer

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

alexandr-penkin/psalm-fixer
===========================

Automatic fixer for Psalm static analysis issues via AST transformations

0.0.11(2w ago)0174↓31.3%MITPHPPHP &gt;=8.3

Since Apr 1Pushed 2w agoCompare

[ Source](https://github.com/Alexandr-Penkin/psalm-fixer)[ Packagist](https://packagist.org/packages/alexandr-penkin/psalm-fixer)[ RSS](/packages/alexandr-penkin-psalm-fixer/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (10)Dependencies (5)Versions (12)Used By (0)

psalm-fixer
===========

[](#psalm-fixer)

Automatic fixing of [Psalm](https://psalm.dev/) static analysis issues via AST transformations using [nikic/php-parser](https://github.com/nikic/PHP-Parser).

psalm-fixer reads Psalm's JSON output, matches each issue to a registered fixer, and rewrites source files using a format-preserving printer so diffs stay minimal.

Installation
------------

[](#installation)

```
composer require alexandr-penkin/psalm-fixer --dev
```

Usage
-----

[](#usage)

```
# Run Psalm with JSON output and pipe to the fixer
vendor/bin/psalm --output-format=json | vendor/bin/psalm-fixer fix -

# Or via file
vendor/bin/psalm --output-format=json > psalm-issues.json
vendor/bin/psalm-fixer fix psalm-issues.json

# Preview without writing (shows a unified diff)
vendor/bin/psalm-fixer fix psalm-issues.json --diff

# Fix only specific issue types
vendor/bin/psalm-fixer fix psalm-issues.json --issue-type=RedundantCast,MissingOverrideAttribute

# Fix only files matching a pattern
vendor/bin/psalm-fixer fix psalm-issues.json --file=src/Foo.php

# Fix issues listed in a Psalm baseline XML file
vendor/bin/psalm-fixer fix --baseline=psalm-baseline.xml
vendor/bin/psalm-fixer fix --baseline=psalm-baseline.xml --diff
```

`fix` is the default command, so `vendor/bin/psalm-fixer psalm-issues.json` also works.

### Options

[](#options)

OptionDescription`--baseline=path.xml`Read issues from a Psalm baseline XML file instead of JSON. Mutually exclusive with the JSON source argument.`--dry-run`Show what would be fixed without modifying files`--diff`Show diff of changes (implies `--dry-run`)`--backup`Create `.bak` files before modifying`--issue-type=X,Y`Fix only the specified issue types (comma-separated)`--file=pattern`Fix only files matching the pattern (comma-separated)### Fixing from a Psalm baseline

[](#fixing-from-a-psalm-baseline)

Psalm's baseline file (`psalm-baseline.xml`) records suppressed issues by file + issue type + code snippet, but without line numbers. With `--baseline`, psalm-fixer resolves each `` snippet to a source line by trim + substring matching in document order — so two identical snippets in the baseline map to the first two matching source lines. If a snippet can't be found (stale baseline) or the referenced file is missing, the entry is skipped with a `Baseline warning:` message instead of failing the run.

Relative `` paths are resolved against the directory containing the baseline XML.

**Limitation:** the baseline format does not include the original Psalm message text. Fixers that rely only on the AST node at the issue line work identically with baseline input — this includes `MissingOverrideAttribute`, `RedundantCast`, `UnusedForeachValue`, the null-safety set, `TypeDoesNotContainNullFixer` (direction is read from the comparison operator), and `RedundantConditionFixer` for literal `if (true)` / `if (false)` cases. Fixers that need the message to disambiguate (e.g. `RedundantConditionFixer` for "is never X" / "can never contain X" patterns, or `ArgumentTypeCoercionFixer`'s expected-type lookup) report those issues as `not fixed` with a clear reason instead of producing a wrong rewrite — for them, prefer running with Psalm's JSON output as the source.

### Other commands

[](#other-commands)

```
# List all registered fixers with descriptions
vendor/bin/psalm-fixer list-fixers
```

Report output
-------------

[](#report-output)

After a run, psalm-fixer prints a summary grouped by:

- **Fixed** — issues successfully rewritten
- **Unfixed** — a fixer matched but could not apply the change (reason included)
- **No fixer** — no fixer is registered for the issue type

Supported Issue Types
---------------------

[](#supported-issue-types)

**CodeQuality** — RedundantCast, RedundantIdentityWithTrue, UnusedForeachValue, UnusedClosureParam

**ClassDesign** — MissingOverrideAttribute, MissingClassConstType, PropertyNotSetInConstructor

**NullSafety** — PossiblyNullReference, PossiblyNullPropertyFetch, PossiblyNullArgument, PossiblyNullArrayAccess

**TypeSafety** — InvalidScalarArgument, RedundantCondition, RedundantConditionGivenDocblockType, TypeDoesNotContainNull, DocblockTypeContradiction, ArgumentTypeCoercion, PropertyTypeCoercion

**Mixed** — MixedArgument, MixedAssignment, MixedMethodCall, MixedReturnStatement, MixedPropertyFetch, MixedArrayAccess

**Docblock** — MismatchingDocblockPropertyType, UnusedPsalmSuppress

**Suppress-only** — LiteralKeyUnshapedArray, ReferenceConstraintViolation, MixedReturnTypeCoercion, UnsafeInstantiation, MixedArgumentTypeCoercion

How it works
------------

[](#how-it-works)

```
Psalm JSON  ─┐
              ├─> Parser   -> PsalmIssue value objects
Baseline XML ─┘
                -> FileProcessor      (groups issues by file, parses AST)
                -> FixerRegistry      (maps issue type -> fixer)
                -> Fixer              (mutates AST, returns FixResult)
                -> format-preserving printer (writes file back)

```

Fixes are applied bottom-up (descending line order) so earlier edits don't shift positions of later ones. When a fixer changes the node type (e.g. `->` to `?->`) the format-preserving printer is bypassed and a full pretty-print is used as fallback.

### Fix strategies

[](#fix-strategies)

Most fixers either rewrite the AST directly (insert assert, unwrap if, drop redundant operand from `&&`, etc.) or fall back to attaching a `@psalm-suppress ` annotation when no safe runtime rewrite exists. The suppress-fallback is used by `ArgumentTypeCoercionFixer`, `PropertyTypeCoercionFixer`, `MixedAssignmentFixer`, and `LiteralKeyUnshapedArrayFixer` for generic / template types and genuinely-mixed values where Psalm cannot infer a narrower type at the call site. `TypeDoesNotContainNullFixer` and `RedundantConditionFixer` also fall back to suppress for docblock-contradiction reports (`DocblockTypeContradiction`, `RedundantConditionGivenDocblockType`) where no `if` exists at the target line — the docblock disagrees with the inferred runtime type and the contradiction can't be safely rewritten without knowing the author's intent.

For mixed-typed arguments where Psalm expects a generic collection (`array`, `list`, `array{…}` shapes), the assert builder degrades to `assert(is_array($var))` — template arguments are lost but the assertion is still valid and lets Psalm rule out `mixed` at the call site.

Adding a new fixer
------------------

[](#adding-a-new-fixer)

1. Create a class under `src/PsalmFixer/Fixer//` extending `AbstractFixer`.
    - For fixers that locate an `if` statement at the issue line, extend `AbstractIfWalkingFixer` instead — it provides the recursive walk (through namespaces, classes, methods, control-flow blocks), `spliceDeadBranch()`, and `&&`-chain helpers (`flattenAnd` / `buildAndChain`). Implement just `tryFixIf()`.
2. Implement `getSupportedTypes()` returning the Psalm issue type strings it handles.
3. Implement `getName()`, `getDescription()`, and `fix()`.
4. Register it in `FixerRegistry::createDefault()`.
5. Add unit tests in `tests/Unit/Fixer/`.

Development
-----------

[](#development)

```
# Run the test suite
vendor/bin/phpunit

# Run a single test
vendor/bin/phpunit tests/Unit/Fixer/RedundantCastFixerTest.php

# Run Psalm on the project itself
vendor/bin/psalm
```

Requirements
------------

[](#requirements)

- PHP &gt;= 8.3
- nikic/php-parser ^5.0
- symfony/console ^6.0 | ^7.0
- phpstan/phpdoc-parser ^1.0 | ^2.0
- Psalm &gt;= 6.15 (for JSON output generation)

License
-------

[](#license)

MIT

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance96

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

11

Last Release

19d ago

PHP version history (2 changes)0.0.1PHP &gt;=8.0

0.0.10PHP &gt;=8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/f8ea22d66d8c5bc0b302cfcab1cb16d560b172b2cc8356b988da61839fa591e9?d=identicon)[AlexandrPenkin](/maintainers/AlexandrPenkin)

---

Top Contributors

[![Alexandr-Penkin](https://avatars.githubusercontent.com/u/7732415?v=4)](https://github.com/Alexandr-Penkin "Alexandr-Penkin (15 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/alexandr-penkin-psalm-fixer/health.svg)

```
[![Health](https://phpackages.com/badges/alexandr-penkin-psalm-fixer/health.svg)](https://phpackages.com/packages/alexandr-penkin-psalm-fixer)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[phparkitect/phparkitect

Enforce architectural constraints in your PHP applications

9134.1M24](/packages/phparkitect-phparkitect)[jolicode/castor

A lightweight and modern task runner. Automate everything. In PHP.

54642.4k4](/packages/jolicode-castor)[rector/rector-src

Instant Upgrade and Automated Refactoring of any PHP code

136400.8k14](/packages/rector-rector-src)[coenjacobs/mozart

Composes all dependencies as a package inside a WordPress plugin

4783.9M23](/packages/coenjacobs-mozart)[brianhenryie/strauss

Prefixes dependencies namespaces so they are unique to your plugin

187406.5k30](/packages/brianhenryie-strauss)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
