docs/manipulating_code/fixing.md
Psalm is good at finding potential issues in large codebases, but once found, it can be something of a gargantuan task to fix all the issues.
It comes with a tool, called Psalter, that helps you fix code.
You can either run it via its binary
vendor/bin/psalter [args]
or via Psalm's binary:
vendor/bin/psalm --alter [args]
Updating code is inherently risky, doing so automatically is even more so. I've added a few features to make it a little more reassuring:
--dry-run.--php-version, so that (for example) you don't add nullable typehints to PHP 7.0 code, or any typehints at all to PHP 5.6 code. --php-version defaults to your current version.--safe-types mode that will only update PHP 7 return typehints with information Psalm has gathered from non-docblock sources of type information (e.g. typehinted params, instanceof checks, other return typehints etc.)--allow-backwards-incompatible-changes=false you can make sure to not create backwards incompatible changesYou can pass in your own manipulation plugins e.g.
vendor/bin/psalter --plugin=vendor/vimeo/psalm/examples/plugins/ClassUnqualifier.php --dry-run
The above example plugin converts all unnecessarily qualified classnames in your code to shorter aliased versions.
This initial release provides support for the following alterations, corresponding to the names of issues Psalm finds.
To fix all of these at once, run vendor/bin/psalter --issues=all
Running vendor/bin/psalter --issues=MissingReturnType --php-version=7.0 on
<?php
function foo() {
return "hello";
}
gives
<?php
function foo() : string {
return "hello";
}
and running vendor/bin/psalter --issues=MissingReturnType --php-version=5.6 on
<?php
function foo() {
return "hello";
}
gives
<?php
/**
* @return string
*/
function foo() {
return "hello";
}
As above, except for closures
Running vendor/bin/psalter --issues=InvalidReturnType on
<?php
/**
* @return int
*/
function foo() {
return "hello";
}
gives
<?php
/**
* @return string
*/
function foo() {
return "hello";
}
There's also support for return typehints, so running vendor/bin/psalter --issues=InvalidReturnType on
<?php
function foo() : int {
return "hello";
}
gives
<?php
function foo() : string {
return "hello";
}
Running vendor/bin/psalter --issues=InvalidNullableReturnType --php-version=7.1 on
<?php
function foo() : string {
return rand(0, 1) ? "hello" : null;
}
gives
<?php
function foo() : ?string {
return rand(0, 1) ? "hello" : null;
}
and running vendor/bin/psalter --issues=InvalidNullableReturnType --php-version=7.0 on
<?php
function foo() : string {
return rand(0, 1) ? "hello" : null;
}
gives
<?php
/**
* @return string|null
*/
function foo() {
return rand(0, 1) ? "hello" : null;
}
Running vendor/bin/psalter --issues=InvalidFalsableReturnType on
<?php
function foo() : string {
return rand(0, 1) ? "hello" : false;
}
gives
<?php
/**
* @return string|false
*/
function foo() {
return rand(0, 1) ? "hello" : false;
}
Running vendor/bin/psalter --issues=MissingParamType on
<?php
class C {
public static function foo($s) : void {
echo $s;
}
}
C::foo("hello");
gives
<?php
class C {
/**
* @param string $s
*/
public static function foo($s) : void {
echo $s;
}
}
C::foo("hello");
Running vendor/bin/psalter --issues=MissingPropertyType on
<?php
class A {
public $foo;
public $bar;
public $baz;
public function __construct()
{
if (rand(0, 1)) {
$this->foo = 5;
} else {
$this->foo = "hello";
}
$this->bar = "baz";
}
public function setBaz() {
$this->baz = [1, 2, 3];
}
}
gives
<?php
class A {
/**
* @var string|int
*/
public $foo;
public string $bar;
/**
* @var array<int, int>|null
* @psalm-var non-empty-list<int>|null
*/
public $baz;
public function __construct()
{
if (rand(0, 1)) {
$this->foo = 5;
} else {
$this->foo = "hello";
}
$this->bar = "baz";
}
public function setBaz() {
$this->baz = [1, 2, 3];
}
}
Given
<?php
class A {}
class B extends A {}
class C extends A {}
class D {}
running vendor/bin/psalter --issues=MismatchingDocblockParamType on
<?php
/**
* @param B|C $first
* @param D $second
*/
function foo(A $first, A $second) : void {}
gives
<?php
/**
* @param B|C $first
* @param A $second
*/
function foo(A $first, A $second) : void {}
Running vendor/bin/psalter --issues=MismatchingDocblockReturnType on
<?php
/**
* @return int
*/
function foo() : string {
return "hello";
}
gives
<?php
/**
* @return string
*/
function foo() : string {
return "hello";
}
Running vendor/bin/psalter --issues=LessSpecificReturnType on
<?php
function foo() : ?string {
return "hello";
}
gives
<?php
function foo() : string {
return "hello";
}
Running vendor/bin/psalter --issues=PossiblyUndefinedVariable on
<?php
function foo()
{
if (rand(0, 1)) {
$a = 5;
}
echo $a;
}
gives
<?php
function foo()
{
$a = null;
if (rand(0, 1)) {
$a = 5;
}
echo $a;
}
Running vendor/bin/psalter --issues=PossiblyUndefinedGlobalVariable on
<?php
if (rand(0, 1)) {
$a = 5;
}
echo $a;
gives
<?php
$a = null;
if (rand(0, 1)) {
$a = 5;
}
echo $a;
This removes private unused methods.
Running vendor/bin/psalter --issues=UnusedMethod on
<?php
class A {
private function foo() : void {}
}
new A();
gives
<?php
class A {
}
new A();
This removes protected/public unused methods.
Running vendor/bin/psalter --issues=PossiblyUnusedMethod on
<?php
class A {
protected function foo() : void {}
public function bar() : void {}
}
new A();
gives
<?php
class A {
}
new A();
This removes private unused properties.
Running vendor/bin/psalter --issues=UnusedProperty on
<?php
class A {
/** @var string */
private $foo;
}
new A();
gives
<?php
class A {
}
new A();
This removes protected/public unused properties.
Running vendor/bin/psalter --issues=PossiblyUnusedProperty on
<?php
class A {
/** @var string */
public $foo;
/** @var string */
protected $bar;
}
new A();
gives
<?php
class A {
}
new A();
This removes unused variables.
Running vendor/bin/psalter --issues=UnusedVariable on
<?php
function foo(string $s) : void {
$a = 5;
$b = 6;
$c = $b += $a -= intval($s);
echo "foo";
}
gives
<?php
function foo(string $s) : void {
echo "foo";
}
This removes unused @var annotations
Running vendor/bin/psalter --issues=UnnecessaryVarAnnotation on
<?php
function foo() : string {
return "hello";
}
/** @var string */
$a = foo();
gives
<?php
function foo() : string {
return "hello";
}
$a = foo();
This aligns child class param names with their parent.
Running vendor/bin/psalter --issues=ParamNameMismatch on
<?php
class A {
public function foo(string $str, bool $b = false) : void {}
}
class AChild extends A {
public function foo(string $string, bool $b = false) : void {
echo $string;
}
}
gives
<?php
class A {
public function foo(string $str, bool $b = false) : void {}
}
class AChild extends A {
public function foo(string $str, bool $b = false) : void {
echo $str;
}
}