1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-15 10:57:08 +01:00
psalm/docs/annotating_code/supported_annotations.md

650 lines
17 KiB
Markdown
Raw Normal View History

2018-01-30 06:14:36 +01:00
# Supported docblock annotations
Psalm supports a wide range of docblock annotations.
## PHPDoc tags
Psalm uses the following PHPDoc tags to understand your code:
2020-02-23 06:41:19 +01:00
- [`@var`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/var.html)
Used for specifying the types of properties and variables@
- [`@return`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/return.html)
2018-01-30 17:31:17 +01:00
Used for specifying the return types of functions, methods and closures
- [`@param`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html)
2018-01-30 17:31:17 +01:00
Used for specifying types of parameters passed to functions, methods and closures
- [`@property`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html)
2018-01-30 17:31:17 +01:00
Used to specify what properties can be accessed on an object that uses `__get` and `__set`
- [`@property-read`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property-read.html)
2018-01-30 17:31:17 +01:00
Used to specify what properties can be read on object that uses `__get`
- [`@property-write`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property-write.html)
2018-01-30 17:31:17 +01:00
Used to specify what properties can be written on object that uses `__set`
- [`@method`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/method.html)
Used to specify which magic methods are available on object that uses `__call`.
- [`@deprecated`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/deprecated.html)
2018-01-30 17:31:17 +01:00
Used to mark functions, methods, classes and interfaces as being deprecated
- [`@internal`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/internal.html)
used to mark classes, functions and properties that are internal to an application or library.
2018-01-30 06:14:36 +01:00
### Off-label usage of the `@var` tag
The `@var` tag is supposed to only be used for properties. Psalm, taking a lead from PHPStorm and other static analysis tools, allows its use inline in the form `@var Type [VariableReference]`.
If `VariableReference` is provided, it should be of the form `$variable` or `$variable->property`. If used above an assignment, Psalm checks whether the `VariableReference` matches the variable being assigned. If they differ, Psalm will assign the `Type` to `VariableReference` and use it in the expression below.
2022-04-27 07:46:13 +02:00
If no `VariableReference` is given, the annotation tells Psalm that the right-hand side of the expression, whether an assignment or a return, is of type `Type`.
2018-01-30 06:14:36 +01:00
```php
<?php
2018-01-30 06:14:36 +01:00
/** @var string */
$a = $_GET['foo'];
/** @var string $b */
$b = $_GET['bar'];
function bat(): string {
/** @var string */
return $_GET['bat'];
}
```
## Psalm-specific tags
There are a number of custom tags that determine how Psalm treats your code.
### `@psalm-consistent-constructor`
See [UnsafeInstantiation](../running_psalm/issues/UnsafeInstantiation.md)
### `@psalm-consistent-templates`
See [UnsafeGenericInstantiation](../running_psalm/issues/UnsafeGenericInstantiation.md)
### `@param-out`, `@psalm-param-out`
2019-01-20 18:50:47 +01:00
This is used to specify that a by-ref type is different from the one that entered. In the function below the first param can be null, but once the function has executed the by-ref value is not null.
```php
<?php
2019-01-20 18:50:47 +01:00
/**
* @param-out string $s
*/
function addFoo(?string &$s) : void {
if ($s === null) {
$s = "hello";
}
$s .= "foo";
}
```
### `@psalm-var`, `@psalm-param`, `@psalm-return`, `@psalm-property`, `@psalm-property-read`, `@psalm-property-write`, `@psalm-method`
2018-01-30 06:14:36 +01:00
2019-02-12 23:27:31 +01:00
When specifying types in a format not supported by phpDocumentor ([but supported by Psalm](#type-syntax)) you may wish to prepend `@psalm-` to the PHPDoc tag, so as to avoid confusing your IDE. If a `@psalm`-prefixed tag is given, Psalm will use it in place of its non-prefixed counterpart.
2018-01-30 06:14:36 +01:00
### `@psalm-ignore-var`
This annotation is used to ignore the `@var` annotation written in the same docblock. Some IDEs don't fully understand complex types like generics. To take advantage of such IDE's auto-completion, you may sometimes want to use explicit `@var` annotations even when psalm can infer the type just fine. This weakens the effectiveness of type checking in many cases since the explicit `@var` annotation overrides the types inferred by psalm. As psalm ignores the `@var` annotation which is co-located with `@psalm-ignore-var`, IDEs can use the type specified by the `@var` for auto-completion, while psalm can still use its own inferred type for type checking.
```php
<?php
/** @return iterable<array-key,\DateTime> $f */
function getTimes(int $n): iterable {
while ($n--) {
yield new \DateTime();
}
};
/**
* @var \Datetime[] $times
* @psalm-ignore-var
*/
$times = getTimes(3);
// this trace shows "iterable<array-key, DateTime>" instead of "array<array-key, Datetime>"
/** @psalm-trace $times */
foreach ($times as $time) {
echo $time->format('Y-m-d H:i:s.u') . PHP_EOL;
}
```
2018-01-30 06:14:36 +01:00
### `@psalm-suppress SomeIssueName`
This annotation is used to suppress issues. It can be used in function docblocks, class docblocks and also inline, applying to the following statement.
Function docblock example:
```php
<?php
2018-01-30 06:14:36 +01:00
/**
* @psalm-suppress PossiblyNullOperand
*/
function addString(?string $s) {
echo "hello " . $s;
}
```
Inline example:
```php
<?php
2018-01-30 06:14:36 +01:00
function addString(?string $s) {
/** @psalm-suppress PossiblyNullOperand */
echo "hello " . $s;
}
```
`@psalm-suppress all` can be used to suppress all issues instead of listing them individually.
### `@psalm-assert`, `@psalm-assert-if-true`, `@psalm-assert-if-false`, `@psalm-if-this-is` and `@psalm-this-out`
2018-06-04 05:38:06 +02:00
See [Adding assertions](adding_assertions.md).
2018-01-30 06:14:36 +01:00
### `@psalm-ignore-nullable-return`
This can be used to tell Psalm not to worry if a function/method returns null. Its a bit of a hack, but occasionally useful for scenarios where you either have a very high confidence of a non-null value, or some other function guarantees a non-null value for that particular code path.
```php
<?php
2018-01-30 06:14:36 +01:00
class Foo {}
function takesFoo(Foo $f): void {}
/** @psalm-ignore-nullable-return */
function getFoo(): ?Foo {
return rand(0, 10000) > 1 ? new Foo() : null;
}
takesFoo(getFoo());
```
### `@psalm-ignore-falsable-return`
This provides the same, but for `false`. Psalm uses this internally for functions like `preg_replace`, which can return false if the given input has encoding errors, but where 99.9% of the time the function operates as expected.
2019-01-30 05:26:37 +01:00
### `@psalm-seal-properties`
2018-01-30 06:14:36 +01:00
2019-01-30 05:26:37 +01:00
If you have a magic property getter/setter, you can use `@psalm-seal-properties` to instruct Psalm to disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations.
2018-01-30 06:14:36 +01:00
```php
<?php
2018-01-30 06:14:36 +01:00
/**
2019-01-30 05:26:37 +01:00
* @property string $foo
* @psalm-seal-properties
*/
class A {
public function __get(string $name): ?string {
if ($name === "foo") {
return "hello";
}
}
public function __set(string $name, $value): void {}
}
$a = new A();
$a->bar = 5; // this call fails
```
### `@psalm-internal`
Used to mark a class, property or function as internal to a given namespace. Psalm treats this slightly differently to
2019-06-28 19:59:09 +02:00
the PHPDoc `@internal` tag. For `@internal`, an issue is raised if the calling code is in a namespace completely
unrelated to the namespace of the calling code, i.e. not sharing the first element of the namespace.
2022-04-27 07:46:13 +02:00
In contrast for `@psalm-internal`, the docblock line must specify a namespace. An issue is raised if the calling code
is not within the given namespace.
2019-09-08 21:21:30 +02:00
```php
<?php
namespace A\B {
/**
* @internal
* @psalm-internal A\B
*/
class Foo { }
}
namespace A\B\C {
class Bat {
public function batBat(): void {
$a = new \A\B\Foo(); // this is fine
}
}
}
namespace A\C {
class Bat {
public function batBat(): void {
$a = new \A\B\Foo(); // error
}
}
}
```
2019-09-08 21:21:30 +02:00
### `@psalm-readonly` and `@readonly`
Used to annotate a property that can only be written to in its defining class's constructor.
```php
<?php
class B {
/** @readonly */
public string $s;
2019-09-08 21:21:30 +02:00
public function __construct(string $s) {
$this->s = $s;
}
}
$b = new B("hello");
echo $b->s;
$b->s = "boo"; // disallowed
```
### `@psalm-mutation-free`
Used to annotate a class method that does not mutate state, either internally or externally of the class's scope.
This requires that the return value depend only on the instance's properties. For example, `random_int` is considered
mutating here because it mutates the random number generator's internal state.
2019-09-08 21:21:30 +02:00
```php
<?php
2019-09-08 21:21:30 +02:00
class D {
private string $s;
2019-09-08 21:21:30 +02:00
public function __construct(string $s) {
$this->s = $s;
}
2019-09-08 21:21:30 +02:00
/**
* @psalm-mutation-free
*/
public function getShort() : string {
return substr($this->s, 0, 5);
}
2019-09-08 21:21:30 +02:00
/**
* @psalm-mutation-free
*/
public function getShortMutating() : string {
$this->s .= "hello"; // this is a bug
return substr($this->s, 0, 5);
}
}
```
### `@psalm-external-mutation-free`
Used to annotate a class method that does not mutate state externally of the class's scope.
2019-09-08 21:21:30 +02:00
```php
<?php
2019-09-08 21:21:30 +02:00
class E {
private string $s;
2019-09-08 21:21:30 +02:00
public function __construct(string $s) {
$this->s = $s;
}
2019-09-08 21:21:30 +02:00
/**
* @psalm-external-mutation-free
*/
public function getShortMutating() : string {
$this->s .= "hello"; // this is fine
return substr($this->s, 0, 5);
}
/**
* @psalm-external-mutation-free
*/
public function save() : void {
file_put_contents("foo.txt", $this->s); // this is a bug
}
}
```
### `@psalm-immutable`
Used to annotate a class where every property is treated by consumers as `@psalm-readonly` and every instance method is treated as `@psalm-mutation-free`.
2019-09-08 21:21:30 +02:00
```php
<?php
/**
* @psalm-immutable
*/
abstract class Foo
{
public string $baz;
abstract public function bar(): int;
}
/**
* @psalm-immutable
*/
final class ChildClass extends Foo
{
public function __construct(string $baz)
{
$this->baz = $baz;
}
public function bar(): int
{
return 0;
}
}
$anonymous = new /** @psalm-immutable */ class extends Foo
{
public string $baz = "B";
public function bar(): int
{
return 1;
}
};
```
2019-09-08 21:25:12 +02:00
### `@psalm-pure`
Used to annotate a [pure function](https://en.wikipedia.org/wiki/Pure_function) - one whose output is just a function of its input.
```php
<?php
2019-09-08 21:25:12 +02:00
class Arithmetic {
/** @psalm-pure */
public static function add(int $left, int $right) : int {
return $left + $right;
}
/** @psalm-pure - this is wrong */
public static function addCumulative(int $left) : int {
/** @var int */
static $i = 0; // this is a side effect, and thus a bug
$i += $left;
return $i;
}
}
echo Arithmetic::add(40, 2);
echo Arithmetic::add(40, 2); // same value is emitted
echo Arithmetic::addCumulative(3); // outputs 3
echo Arithmetic::addCumulative(3); // outputs 6
```
On the other hand, `pure-callable` can be used to denote a callable which needs to be pure.
```php
/**
* @param pure-callable(mixed): int $callback
*/
function foo(callable $callback) {...}
// this fails since random_int is not pure
foo(
/** @param mixed $p */
fn($p) => random_int(1, 2)
);
```
2020-02-02 21:38:41 +01:00
### `@psalm-allow-private-mutation`
2020-02-02 21:38:41 +01:00
Used to annotate readonly properties that can be mutated in a private context. With this, public properties can be read from another class but only be mutated within a method of its own class.
```php
<?php
class Counter {
2020-02-02 21:38:41 +01:00
/**
* @readonly
* @psalm-allow-private-mutation
*/
public int $count = 0;
public function increment() : void {
$this->count++;
}
}
$counter = new Counter();
echo $counter->count; // outputs 0
2020-02-02 21:03:17 +01:00
$counter->increment(); // Method can mutate property
echo $counter->count; // outputs 1
2020-02-02 21:38:41 +01:00
$counter->count = 5; // This will fail, as it's mutating a property directly
```
### `@psalm-readonly-allow-private-mutation`
This is a shorthand for the property annotations `@readonly` and `@psalm-allow-private-mutation`.
```php
<?php
class Counter {
/**
* @psalm-readonly-allow-private-mutation
*/
public int $count = 0;
public function increment() : void {
$this->count++;
}
}
$counter = new Counter();
echo $counter->count; // outputs 0
$counter->increment(); // Method can mutate property
echo $counter->count; // outputs 1
$counter->count = 5; // This will fail, as it's mutating a property directly
```
### `@psalm-trace`
You can use this annotation to trace inferred type (applied to the *next* statement).
```php
<?php
/** @psalm-trace $username */
$username = $_GET['username']; // prints something like "test.php:4 $username: mixed"
```
*Note*: it throws [special low-level issue](../running_psalm/issues/Trace.md), so you have to set errorLevel to 1, override it in config or invoke Psalm with `--show-info=true`.
### `@psalm-check-type`
You can use this annotation to ensure the inferred type matches what you expect.
```php
<?php
/** @psalm-check-type $foo = int */
$foo = 1; // No issue
/** @psalm-check-type $bar = int */
$bar = "not-an-int"; // Checked variable $bar = int does not match $bar = 'not-an-int'
```
### `@psalm-check-type-exact`
Like `@psalm-check-type`, but checks the exact type of the variable without allowing subtypes.
```php
<?php
/** @psalm-check-type-exact $foo = int */
$foo = 1; // Checked variable $foo = int does not match $foo = 1
```
2020-06-17 02:55:39 +02:00
### `@psalm-taint-*`
See [Security Analysis annotations](../security_analysis/annotations.md).
### `@psalm-type`
This allows you to define an alias for another type.
```php
<?php
/**
* @psalm-type PhoneType = array{phone: string}
*/
class Phone {
/**
* @psalm-return PhoneType
*/
public function toArray(): array {
return ["phone" => "Nokia"];
}
}
```
### `@psalm-import-type`
You can use this annotation to import a type defined with [`@psalm-type`](#psalm-type) if it was defined somewhere else.
```php
<?php
/**
* @psalm-import-type PhoneType from Phone
*/
class User {
/**
* @psalm-return PhoneType
*/
public function toArray(): array {
return array_merge([], (new Phone())->toArray());
}
}
```
You can also alias a type when you import it:
```php
<?php
/**
* @psalm-import-type PhoneType from Phone as MyPhoneTypeAlias
*/
class User {
/**
* @psalm-return MyPhoneTypeAlias
*/
public function toArray(): array {
return array_merge([], (new Phone())->toArray());
}
}
```
### `@psalm-require-extends`
The `@psalm-require-extends` annotation allows you to define a requirements that a trait imposes on the using class.
```php
<?php
abstract class DatabaseModel {
// methods, properties, etc.
}
/**
* @psalm-require-extends DatabaseModel
*/
trait SoftDeletingTrait {
// useful but scoped functionality, that depends on methods/properties from DatabaseModel
}
class MyModel extends DatabaseModel {
// valid
use SoftDeletingTrait;
}
class NormalClass {
// triggers an error
use SoftDeletingTrait;
}
```
### `@psalm-require-implements`
Behaves the same way as `@psalm-require-extends`, but for interfaces.
### `@no-named-arguments`
This will prevent access to the function or method tagged with named parameters (by emitting a `NamedArgumentNotAllowed` issue).
Incidentally, it will change the inferred type for the following code:
```php
<?php
function a(int ...$a){
var_dump($a);
}
```
2022-04-27 07:46:13 +02:00
The type of `$a` is `array<array-key, int>` without `@no-named-arguments` but becomes `list<int>` with it, because it excludes the case where the offset would be a string with the name of the parameter
### `@psalm-ignore-variable-property` and `@psalm-ignore-variable-method`
Instructs Psalm to ignore variable property fetch / variable method call when looking for dead code.
```php
class Foo
{
// this property can be deleted by Psalter,
// as potential reference in get() is ignored
public string $bar = 'bar';
public function get(string $name): mixed
{
/** @psalm-ignore-variable-property */
return $this->{$name};
}
}
```
When Psalm encounters variable property, it treats all properties in given class as potentially referenced.
With `@psalm-ignore-variable-property` annotation, this reference is ignored.
While `PossiblyUnusedProperty` would be emitted in both cases, using `@psalm-ignore-variable-property`
would allow [Psalter](../manipulating_code/fixing.md) to delete `Foo::$bar`.
`@psalm-ignore-variable-method` behaves the same way, but for variable method calls.
### `@psalm-yield`
Used to specify the type of value which will be sent back to a generator when an annotated object instance is yielded.
```php
<?php
/**
* @template-covariant TValue
* @psalm-yield TValue
*/
interface Promise {}
/**
* @template-covariant TValue
* @template-implements Promise<TValue>
*/
class Success implements Promise {
/**
* @psalm-param TValue $value
*/
public function __construct($value) {}
}
/**
* @return Promise<string>
*/
function fetch(): Promise {
return new Success('{"data":[]}');
}
function (): Generator {
$data = yield fetch();
// this is fine, Psalm knows that $data is a string
return json_decode($data);
};
```
This annotation supports only generic types, meaning that e.g. `@psalm-yield string` would be ignored.
2018-01-30 06:14:36 +01:00
## Type Syntax
Psalm supports PHPDocs [type syntax](https://docs.phpdoc.org/latest/guide/guides/types.html), and also the [proposed PHPDoc PSR type syntax](https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#appendix-a-types).
2018-01-30 06:14:36 +01:00
2019-01-02 19:56:43 +01:00
A detailed write-up is found in [Typing in Psalm](typing_in_psalm.md)