Install psalm as dev, sync scripts updates
This commit is contained in:
72
vendor/vimeo/psalm/docs/README.md
vendored
Normal file
72
vendor/vimeo/psalm/docs/README.md
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# About Psalm
|
||||
|
||||
Psalm is a static analysis tool that attempts to dig into your program and find as many type-related bugs as possible.
|
||||
|
||||
It has a few features that go further than other similar tools:
|
||||
|
||||
- **Mixed type warnings**<br />
|
||||
If Psalm cannot infer a type for an expression then it uses a `mixed` placeholder type. `mixed` types can sometimes mask bugs, so keeping track of them helps you avoid a number of common pitfalls.
|
||||
|
||||
- **Intelligent logic checks**<br />
|
||||
Psalm keeps track of logical assertions made about your code, so `if ($a && $a) {}` and `if ($a && !$a) {}` are both treated as issues. Psalm also keeps track of logical assertions made in prior code paths, preventing issues like `if ($a) {} elseif ($a) {}`.
|
||||
|
||||
- **Property initialisation checks**<br />
|
||||
Psalm checks that all properties of a given object have values after the constructor is called.
|
||||
|
||||
- **Taint analysis**<br />
|
||||
Psalm can [detect security vulnerabilities](https://psalm.dev/articles/detect-security-vulnerabilities-with-psalm) in your code.
|
||||
|
||||
- **Language Server**<br />
|
||||
Psalm has a Language Server that’s [compatible with a range of different IDEs](https://psalm.dev/docs/running_psalm/language_server/).
|
||||
|
||||
- **Automatic fixes**<br />
|
||||
Psalm can [fix many of the issues it finds automatically](https://psalm.dev/docs/manipulating_code/fixing/).
|
||||
|
||||
- **Automatic refactoring**<br />
|
||||
Psalm can also [perform simple refactors](https://psalm.dev/docs/manipulating_code/refactoring/) from the command line.
|
||||
|
||||
## Example output
|
||||
|
||||
Given a file `implode_strings.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$a = ['foo', 'bar'];
|
||||
echo implode($a, ' ');
|
||||
```
|
||||
|
||||
```bash
|
||||
> ./vendor/bin/psalm implode_strings.php
|
||||
ERROR: InvalidArgument - somefile.php:3:14 - Argument 1 of implode expects `string`, `array` provided (see https://psalm.dev/004)
|
||||
```
|
||||
|
||||
## Inspirations
|
||||
|
||||
There are two main inspirations for Psalm:
|
||||
|
||||
- Etsy's [Phan](https://github.com/etsy/phan), which uses nikic's [php-ast](https://github.com/nikic/php-ast) extension to create an abstract syntax tree
|
||||
- Facebook's [Hack](http://hacklang.org/), a PHP-like language that supports many advanced typing features natively, so docblocks aren't necessary.
|
||||
|
||||
## Index
|
||||
|
||||
- Running Psalm:
|
||||
- [Installation](running_psalm/installation.md)
|
||||
- [Configuration](running_psalm/configuration.md)
|
||||
- Plugins
|
||||
- [Using plugins](running_psalm/plugins/using_plugins.md)
|
||||
- [Authoring plugins](running_psalm/plugins/authoring_plugins.md)
|
||||
- [How Psalm represents types](running_psalm/plugins/plugins_type_system.md)
|
||||
- [Command line usage](running_psalm/command_line_usage.md)
|
||||
- [IDE support](running_psalm/language_server.md)
|
||||
- Handling errors:
|
||||
- [Dealing with code issues](running_psalm/dealing_with_code_issues.md)
|
||||
- [Issue Types](running_psalm/issues.md)
|
||||
- [Checking non-PHP files](running_psalm/checking_non_php_files.md)
|
||||
- Annotating code:
|
||||
- [Typing in Psalm](annotating_code/typing_in_psalm.md)
|
||||
- [Supported Annotations](annotating_code/supported_annotations.md)
|
||||
- [Template Annotations](annotating_code/templated_annotations.md)
|
||||
- Manipulating code:
|
||||
- [Fixing code](manipulating_code/fixing.md)
|
||||
- [Refactoring code](manipulating_code/refactoring.md)
|
||||
|
||||
223
vendor/vimeo/psalm/docs/annotating_code/adding_assertions.md
vendored
Normal file
223
vendor/vimeo/psalm/docs/annotating_code/adding_assertions.md
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
# Adding assertions
|
||||
|
||||
Psalm has five docblock annotations that allow you to specify that a function verifies facts about variables and properties:
|
||||
|
||||
- `@psalm-assert` (used when throwing an exception)
|
||||
- `@psalm-assert-if-true`/`@psalm-assert-if-false` (used when returning a `bool`)
|
||||
- `@psalm-if-this-is`/`@psalm-this-out` (used when calling a method)
|
||||
|
||||
A list of acceptable assertions [can be found here](assertion_syntax.md).
|
||||
|
||||
## Examples
|
||||
|
||||
If you have a class that verified its input is an array of strings, you can make that clear to Psalm:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert string[] $arr */
|
||||
function validateStringArray(array $arr) : void {
|
||||
foreach ($arr as $s) {
|
||||
if (!is_string($s)) {
|
||||
throw new UnexpectedValueException('Invalid value ' . gettype($s));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This enables you to call the `validateStringArray` function on some data and have Psalm understand that the given data *must* be an array of strings:
|
||||
|
||||
```php
|
||||
<?php
|
||||
function takesString(string $s) : void {}
|
||||
function takesInt(int $s) : void {}
|
||||
|
||||
function takesArray(array $arr) : void {
|
||||
takesInt($arr[0]); // this is fine
|
||||
|
||||
validateStringArray($arr);
|
||||
|
||||
takesInt($arr[0]); // this is an error
|
||||
|
||||
foreach ($arr as $a) {
|
||||
takesString($a); // this is fine
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, `@psalm-assert-if-true` and `@psalm-assert-if-false` will filter input if the function/method returns `true` and `false` respectively:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
public function isValid() : bool {
|
||||
return (bool) rand(0, 1);
|
||||
}
|
||||
}
|
||||
class B extends A {
|
||||
public function bar() : void {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-assert-if-true B $a
|
||||
*/
|
||||
function isValidB(A $a) : bool {
|
||||
return $a instanceof B && $a->isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-assert-if-false B $a
|
||||
*/
|
||||
function isInvalidB(A $a) : bool {
|
||||
return !$a instanceof B || !$a->isValid();
|
||||
}
|
||||
|
||||
function takesA(A $a) : void {
|
||||
if (isValidB($a)) {
|
||||
$a->bar();
|
||||
}
|
||||
|
||||
if (isInvalidB($a)) {
|
||||
// do something
|
||||
} else {
|
||||
$a->bar();
|
||||
}
|
||||
|
||||
$a->bar(); //error
|
||||
}
|
||||
```
|
||||
|
||||
As well as getting Psalm to understand that the given data must be a certain type, you can also show that a variable must be not null:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @psalm-assert !null $value
|
||||
*/
|
||||
function assertNotNull($value): void {
|
||||
// Some check that will mean the method will only complete if $value is not null.
|
||||
}
|
||||
```
|
||||
|
||||
And you can check on null values:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @psalm-assert-if-true null $value
|
||||
*/
|
||||
function isNull($value): bool {
|
||||
return ($value === null);
|
||||
}
|
||||
```
|
||||
|
||||
### Asserting return values of methods
|
||||
|
||||
You can also use the `@psalm-assert-if-true` and `@psalm-assert-if-false` annotations to assert return values of
|
||||
methods inside classes. As you can see, Psalm even allows you to specify multiple annotations in the same DocBlock:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Result {
|
||||
/**
|
||||
* @var ?Exception
|
||||
*/
|
||||
private $exception;
|
||||
|
||||
/**
|
||||
* @psalm-assert-if-true Exception $this->exception
|
||||
* @psalm-assert-if-true Exception $this->getException()
|
||||
*/
|
||||
public function hasException(): bool {
|
||||
return $this->exception !== null;
|
||||
}
|
||||
|
||||
public function getException(): ?Exception {
|
||||
return $this->exception;
|
||||
}
|
||||
|
||||
public function foo(): void {
|
||||
if( $this->hasException() ) {
|
||||
// Psalm now knows that $this->exception is an instance of Exception
|
||||
echo $this->exception->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = new Result;
|
||||
|
||||
if( $result->hasException() ) {
|
||||
// Psalm now knows that $result->getException() will return an instance of Exception
|
||||
echo $result->getException()->getMessage();
|
||||
}
|
||||
```
|
||||
|
||||
Please note that the example above only works if you enable [method call memoization](https://psalm.dev/docs/running_psalm/configuration/#memoizemethodcallresults)
|
||||
in the config file or annotate the class as [immutable](https://psalm.dev/docs/annotating_code/supported_annotations/#psalm-immutable).
|
||||
|
||||
|
||||
You can use `@psalm-this-out` to change the template arguments of a method after a method call, to reflect changes to the object's internal state.
|
||||
You can also make assertions on the object's template arguments using `@psalm-if-this-is`.
|
||||
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class a {
|
||||
/**
|
||||
* @var list<T>
|
||||
*/
|
||||
private array $data;
|
||||
/**
|
||||
* @param T $data
|
||||
*/
|
||||
public function __construct($data) {
|
||||
$this->data = [$data];
|
||||
}
|
||||
/**
|
||||
* @template NewT
|
||||
*
|
||||
* @param NewT $data
|
||||
*
|
||||
* @psalm-this-out self<T|NewT>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addData($data) {
|
||||
/** @var self<T|NewT> $this */
|
||||
$this->data []= $data;
|
||||
}
|
||||
/**
|
||||
* @template NewT
|
||||
*
|
||||
* @param NewT $data
|
||||
*
|
||||
* @psalm-this-out self<NewT>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setData($data) {
|
||||
/** @var self<NewT> $this */
|
||||
$this->data = [$data];
|
||||
}
|
||||
/**
|
||||
* @psalm-if-this-is a<int>
|
||||
*/
|
||||
public function test(): void {
|
||||
}
|
||||
}
|
||||
|
||||
$i = new a(123);
|
||||
// OK - $i is a<123>
|
||||
$i->test();
|
||||
|
||||
$i->addData(321);
|
||||
// OK - $i is a<123|321>
|
||||
$i->test();
|
||||
|
||||
$i->setData("test");
|
||||
// IfThisIsMismatch - Class is not a<int> as required by psalm-if-this-is
|
||||
$i->test();
|
||||
```
|
||||
127
vendor/vimeo/psalm/docs/annotating_code/assertion_syntax.md
vendored
Normal file
127
vendor/vimeo/psalm/docs/annotating_code/assertion_syntax.md
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# Assertion syntax
|
||||
|
||||
Psalm’s [assertion annotation](adding_assertions.md) supports a number of different assertions.
|
||||
|
||||
Psalm assertions are of the form
|
||||
|
||||
`@psalm-assert(-if-true|-if-false)? (Assertion) (Variable or Property)`
|
||||
|
||||
`Assertion` here can have many forms:
|
||||
|
||||
## Regular assertions
|
||||
|
||||
### is_xxx assertions
|
||||
|
||||
Most `is_xxx` PHP functions have companion assertions e.g. `int` for `is_int`. Here's the full list:
|
||||
|
||||
- `int`
|
||||
- `float`
|
||||
- `string`
|
||||
- `bool`
|
||||
- `scalar`
|
||||
- `callable`
|
||||
- `countable`
|
||||
- `array`
|
||||
- `iterable`
|
||||
- `numeric`
|
||||
- `resource`
|
||||
- `object`
|
||||
- `null`
|
||||
|
||||
So a custom version `is_int` could be annotated in Psalm as
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert-if-true int $x */
|
||||
function custom_is_int($x) {
|
||||
return is_int($x);
|
||||
}
|
||||
```
|
||||
|
||||
### Object type assertions
|
||||
|
||||
Any class can be used as an assertion e.g.
|
||||
|
||||
`@psalm-assert SomeObjectType $foo`
|
||||
|
||||
### Generic assertions
|
||||
|
||||
Generic type parameters can also now be asserted e.g.
|
||||
|
||||
`@psalm-assert array<int, string> $foo`
|
||||
|
||||
## Negated assertions
|
||||
|
||||
Any assertion above can be negated:
|
||||
|
||||
This asserts that `$foo` is not an `int`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert !int $foo */
|
||||
```
|
||||
|
||||
This asserts that `$bar` is not an object of type `SomeObjectType`:
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert !SomeObjectType $bar */
|
||||
```
|
||||
|
||||
## Bool assertions
|
||||
|
||||
This asserts that `$bar` is `true`:
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert true $bar */
|
||||
```
|
||||
|
||||
This asserts that `$bar` is not `false`:
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert !false $bar */
|
||||
```
|
||||
|
||||
## Equality assertions
|
||||
|
||||
Psalm also supports the equivalent of `assert($some_int === $other_int)` in the form
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert =int $some_int */
|
||||
```
|
||||
|
||||
There are two differences between the above assertion and
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert int $some_int */
|
||||
```
|
||||
|
||||
Firstly, the negation of `=int` has no meaning:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-assert-if-true =int $x */
|
||||
function equalsFive($x) {
|
||||
return is_int($x) && $x === 5;
|
||||
}
|
||||
|
||||
function foo($y) : void {
|
||||
if (equalsFive($y)) {
|
||||
// $y is definitely an int
|
||||
} else {
|
||||
// $y might be an int, but it might not
|
||||
}
|
||||
}
|
||||
|
||||
function bar($y) : void {
|
||||
if (is_int($y)) {
|
||||
// $y is definitely an int
|
||||
} else {
|
||||
// $y is definitely not an int
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Secondly, calling `equalsFive($some_int)` is not a `RedundantCondition` in Psalm, whereas calling `is_int($some_int)` is.
|
||||
|
||||
|
||||
664
vendor/vimeo/psalm/docs/annotating_code/supported_annotations.md
vendored
Normal file
664
vendor/vimeo/psalm/docs/annotating_code/supported_annotations.md
vendored
Normal file
@@ -0,0 +1,664 @@
|
||||
# Supported docblock annotations
|
||||
|
||||
Psalm supports a wide range of docblock annotations.
|
||||
|
||||
## PHPDoc tags
|
||||
|
||||
Psalm uses the following PHPDoc tags to understand your code:
|
||||
|
||||
- [`@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)
|
||||
Used for specifying the return types of functions, methods and closures
|
||||
- [`@param`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html)
|
||||
Used for specifying types of parameters passed to functions, methods and closures
|
||||
- [`@property`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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.
|
||||
|
||||
### 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.
|
||||
|
||||
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`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @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`
|
||||
|
||||
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
|
||||
/**
|
||||
* @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`
|
||||
|
||||
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.
|
||||
|
||||
### `@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;
|
||||
}
|
||||
```
|
||||
|
||||
### `@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
|
||||
/**
|
||||
* @psalm-suppress PossiblyNullOperand
|
||||
*/
|
||||
function addString(?string $s) {
|
||||
echo "hello " . $s;
|
||||
}
|
||||
```
|
||||
|
||||
Inline example:
|
||||
|
||||
```php
|
||||
<?php
|
||||
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`
|
||||
|
||||
See [Adding assertions](adding_assertions.md).
|
||||
|
||||
### `@psalm-ignore-nullable-return`
|
||||
|
||||
This can be used to tell Psalm not to worry if a function/method returns null. It’s 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
|
||||
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.
|
||||
|
||||
### `@psalm-seal-properties`
|
||||
|
||||
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.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `@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;
|
||||
|
||||
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.
|
||||
|
||||
```php
|
||||
<?php
|
||||
class D {
|
||||
private string $s;
|
||||
|
||||
public function __construct(string $s) {
|
||||
$this->s = $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-mutation-free
|
||||
*/
|
||||
public function getShort() : string {
|
||||
return substr($this->s, 0, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
||||
```php
|
||||
<?php
|
||||
class E {
|
||||
private string $s;
|
||||
|
||||
public function __construct(string $s) {
|
||||
$this->s = $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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`.
|
||||
|
||||
```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;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### `@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
|
||||
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)
|
||||
);
|
||||
```
|
||||
|
||||
### `@psalm-allow-private-mutation`
|
||||
|
||||
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 {
|
||||
/**
|
||||
* @readonly
|
||||
* @psalm-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-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
|
||||
```
|
||||
|
||||
### `@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 the 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);
|
||||
}
|
||||
```
|
||||
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.
|
||||
|
||||
### `@psalm-api`
|
||||
|
||||
Used to tell Psalm that a class is used, even if no references to it can be
|
||||
found. Unused issues will be suppressed.
|
||||
|
||||
For example, in frameworks, controllers are often invoked "magically" without
|
||||
any explicit references to them in your code. You should mark these classes with
|
||||
`@psalm-api`.
|
||||
```php
|
||||
/**
|
||||
* @psalm-api
|
||||
*/
|
||||
class UnreferencedClass {}
|
||||
```
|
||||
|
||||
## Type Syntax
|
||||
|
||||
Psalm supports PHPDoc’s [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).
|
||||
|
||||
A detailed write-up is found in [Typing in Psalm](typing_in_psalm.md)
|
||||
454
vendor/vimeo/psalm/docs/annotating_code/templated_annotations.md
vendored
Normal file
454
vendor/vimeo/psalm/docs/annotating_code/templated_annotations.md
vendored
Normal file
@@ -0,0 +1,454 @@
|
||||
# Templating
|
||||
|
||||
Docblocks allow you to tell Psalm some simple information about how your code works. For example `@return int` in a function return type tells Psalm that a function should return an `int` and `@return MyContainer` tells Psalm that a function should return an instance of a user-defined class `MyContainer`. In either case, Psalm can check that the function actually returns those types _and_ that anything calling that function uses its returned value properly.
|
||||
|
||||
Templated types allow you to tell Psalm even more information about how your code works.
|
||||
|
||||
Let's look at a simple class `MyContainer`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class MyContainer {
|
||||
private $value;
|
||||
|
||||
public function __construct($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When Psalm handles the return type of `$my_container->getValue()` it doesn't know what it's getting out, because the value can be arbitrary.
|
||||
|
||||
Templated annotations provide us with a workaround - we can define a generic/templated param `T` that is a placeholder for the value inside `MyContainer`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class MyContainer {
|
||||
/** @var T */
|
||||
private $value;
|
||||
|
||||
/** @param T $value */
|
||||
public function __construct($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/** @return T */
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we can substitute values for that templated param when we reference `MyContainer` in docblocks e.g. `@return MyContainer<int>`. This tells Psalm to substitute `T` for `int` when evaluating that return type, effectively treating it as a class that looks like
|
||||
|
||||
```php
|
||||
<?php
|
||||
class One_off_instance_of_MyContainer {
|
||||
/** @var int */
|
||||
private $value;
|
||||
|
||||
/** @param int $value */
|
||||
public function __construct($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This pattern can be used in a large number of different situations like mocking, collections, iterators and loading arbitrary objects. Psalm has a large number of annotations to make it easy to use templated types in your codebase.
|
||||
|
||||
## `@template`, `@psalm-template`
|
||||
|
||||
The `@template`/`@psalm-template` tag allows classes and functions to declare a generic type parameter.
|
||||
|
||||
As a very simple example, this function returns whatever is passed in:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T
|
||||
* @psalm-param T $t
|
||||
* @return T
|
||||
*/
|
||||
function mirror($t) {
|
||||
return $t;
|
||||
}
|
||||
|
||||
$a = 5;
|
||||
$b = mirror($a); // Psalm knows the result is an int
|
||||
|
||||
$c = "foo";
|
||||
$d = mirror($c); // Psalm knows the result is string
|
||||
```
|
||||
|
||||
Psalm also uses `@template` annotations in its stubbed versions of PHP array functions e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* Takes one array with keys and another with values and combines them
|
||||
*
|
||||
* @template TKey
|
||||
* @template TValue
|
||||
*
|
||||
* @param array<mixed, TKey> $arr
|
||||
* @param array<mixed, TValue> $arr2
|
||||
* @return array<TKey, TValue>
|
||||
*/
|
||||
function array_combine(array $arr, array $arr2) {}
|
||||
```
|
||||
|
||||
### Notes
|
||||
- `@template` tag order matters for class docblocks, as they dictate the order in which those generic parameters are referenced in docblocks.
|
||||
- The names of your templated types (e.g. `TKey`, `TValue`) don't matter outside the scope of the class or function in which they're declared.
|
||||
|
||||
## @param class-string<T>
|
||||
|
||||
Psalm also allows you to parameterize class types
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T of Foo
|
||||
* @psalm-param class-string<T> $class
|
||||
* @return T
|
||||
*/
|
||||
function instantiator(string $class) {
|
||||
return new $class();
|
||||
}
|
||||
|
||||
class Foo {
|
||||
public final function __construct() {}
|
||||
}
|
||||
|
||||
class FooChild extends Foo {}
|
||||
|
||||
$r = instantiator(FooChild::class);
|
||||
// Psalm knows $r is an object of type FooChild
|
||||
```
|
||||
|
||||
## Template inheritance
|
||||
|
||||
Psalm allows you to extend templated classes with `@extends`/`@template-extends`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class ParentClass {}
|
||||
|
||||
/**
|
||||
* @extends ParentClass<int>
|
||||
*/
|
||||
class ChildClass extends ParentClass {}
|
||||
```
|
||||
|
||||
similarly you can implement interfaces with `@implements`/`@template-implements`
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
interface IFoo {}
|
||||
|
||||
/**
|
||||
* @implements IFoo<int>
|
||||
*/
|
||||
class Foo implements IFoo {}
|
||||
```
|
||||
|
||||
and import traits with `@use`/`@template-use`
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
trait MyTrait {}
|
||||
|
||||
class Foo {
|
||||
/**
|
||||
* @use MyTrait<int>
|
||||
*/
|
||||
use MyTrait;
|
||||
}
|
||||
```
|
||||
|
||||
You can also extend one templated class with another, e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T1
|
||||
*/
|
||||
class ParentClass {}
|
||||
|
||||
/**
|
||||
* @template T2
|
||||
* @extends ParentClass<T2>
|
||||
*/
|
||||
class ChildClass extends ParentClass {}
|
||||
```
|
||||
|
||||
## Template constraints
|
||||
|
||||
You can use `@template of <type>` to restrict input. For example, to restrict to a given class you can use
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Foo {}
|
||||
class FooChild extends Foo {}
|
||||
|
||||
/**
|
||||
* @template T of Foo
|
||||
* @psalm-param T $t
|
||||
* @return array<int, T>
|
||||
*/
|
||||
function makeArray($t) {
|
||||
return [$t];
|
||||
}
|
||||
$a = makeArray(new Foo()); // typed as array<int, Foo>
|
||||
$b = makeArray(new FooChild()); // typed as array<int, FooChild>
|
||||
$c = makeArray(new stdClass()); // type error
|
||||
```
|
||||
|
||||
Templated types aren't limited to key-value pairs, and you can re-use templates across multiple arguments of a template-supporting type:
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template T0 of array-key
|
||||
*
|
||||
* @template-implements IteratorAggregate<T0, int>
|
||||
*/
|
||||
abstract class Foo implements IteratorAggregate {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $rand_min;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $rand_max;
|
||||
|
||||
public function __construct(int $rand_min, int $rand_max) {
|
||||
$this->rand_min = $rand_min;
|
||||
$this->rand_max = $rand_max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<T0, int, mixed, T0>
|
||||
*/
|
||||
public function getIterator() : Generator {
|
||||
$j = random_int($this->rand_min, $this->rand_max);
|
||||
for($i = $this->rand_min; $i <= $j; $i += 1) {
|
||||
yield $this->getFuzzyType($i) => $i ** $i;
|
||||
}
|
||||
|
||||
return $this->getFuzzyType($j);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return T0
|
||||
*/
|
||||
abstract protected function getFuzzyType(int $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template-extends Foo<int>
|
||||
*/
|
||||
class Bar extends Foo {
|
||||
protected function getFuzzyType(int $i) : int {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template-extends Foo<string>
|
||||
*/
|
||||
class Baz extends Foo {
|
||||
protected function getFuzzyType(int $i) : string {
|
||||
return static::class . '[' . $i . ']';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Template covariance
|
||||
|
||||
Imagine you have code like this:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Animal {}
|
||||
class Dog extends Animal {}
|
||||
class Cat extends Animal {}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class Collection {
|
||||
/**
|
||||
* @var array<int, T>
|
||||
*/
|
||||
public array $list;
|
||||
|
||||
/**
|
||||
* @param array<int, T> $list
|
||||
*/
|
||||
public function __construct(array $list) {
|
||||
$this->list = $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param T $t
|
||||
*/
|
||||
public function add($t) : void {
|
||||
$this->list[] = $t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<Animal> $collection
|
||||
*/
|
||||
function addAnimal(Collection $collection) : void {
|
||||
$collection->add(new Cat());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<Dog> $dog_collection
|
||||
*/
|
||||
function takesDogList(Collection $dog_collection) : void {
|
||||
addAnimal($dog_collection);
|
||||
}
|
||||
```
|
||||
|
||||
That last call `addAnimal($dog_collection)` breaks the type of the collection – suddenly a collection of dogs becomes a collection of dogs _or_ cats. That is bad.
|
||||
|
||||
To prevent this, Psalm emits an error when calling `addAnimal($dog_collection)` saying "addAnimal expects a `Collection<Animal>`, but `Collection<Dog>` was passed". If you haven't encountered this rule before it's probably confusing to you – any function that accepted an `Animal` would be happy to accept a subtype thereof. But as we see in the example above, doing so can lead to problems.
|
||||
|
||||
But there are also times where it's perfectly safe to pass template param subtypes:
|
||||
|
||||
```php
|
||||
<?php
|
||||
abstract class Animal {
|
||||
abstract public function getNoise() : string;
|
||||
}
|
||||
class Dog extends Animal {
|
||||
public function getNoise() : string { return "woof"; }
|
||||
}
|
||||
class Cat extends Animal {
|
||||
public function getNoise() : string { return "miaow"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class Collection {
|
||||
/** @var array<int, T> */
|
||||
public array $list = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<Animal> $collection
|
||||
*/
|
||||
function getNoises(Collection $collection) : void {
|
||||
foreach ($collection->list as $animal) {
|
||||
echo $animal->getNoise();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<Dog> $dog_collection
|
||||
*/
|
||||
function takesDogList(Collection $dog_collection) : void {
|
||||
getNoises($dog_collection);
|
||||
}
|
||||
```
|
||||
|
||||
Here we're not doing anything bad – we're just iterating over an array of objects. But Psalm still gives that same basic error – "getNoises expects a `Collection<Animal>`, but `Collection<Dog>` was passed".
|
||||
|
||||
We can tell Psalm that it's safe to pass subtypes for the templated param `T` by using the annotation `@template-covariant T` (or `@psalm-template-covariant T`):
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template-covariant T
|
||||
*/
|
||||
class Collection {
|
||||
/** @var array<int, T> */
|
||||
public array $list = [];
|
||||
}
|
||||
```
|
||||
|
||||
Doing this for the above example produces no errors: [https://psalm.dev/r/5254af7a8b](https://psalm.dev/r/5254af7a8b)
|
||||
|
||||
But `@template-covariant` doesn't get rid of _all_ errors – if you add it to the first example, you get a new error – [https://psalm.dev/r/0fcd699231](https://psalm.dev/r/0fcd699231) – complaining that you're attempting to use a covariant template parameter for function input. That’s no good, as it means you're likely altering the collection somehow (which is, again, a violation).
|
||||
|
||||
### But what about immutability?
|
||||
|
||||
Psalm has [comprehensive support for declaring functional immutability](https://psalm.dev/articles/immutability-and-beyond).
|
||||
|
||||
If we make sure that the class is immutable, we can declare a class with an `add` method that still takes a covariant param as input, but which does not modify the collection at all, instead returning a new one:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @template-covariant T
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class Collection {
|
||||
/**
|
||||
* @var array<int, T>
|
||||
*/
|
||||
public array $list = [];
|
||||
|
||||
/**
|
||||
* @param array<int, T> $list
|
||||
*/
|
||||
public function __construct(array $list) {
|
||||
$this->list = $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param T $t
|
||||
* @return Collection<T>
|
||||
*/
|
||||
public function add($t) : Collection {
|
||||
return new Collection(array_merge($this->list, [$t]));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is perfectly valid, and Psalm won't complain.
|
||||
|
||||
## Builtin templated classes and interfaces
|
||||
|
||||
Psalm has support for a number of builtin classes and interfaces that you can extend/implement in your own code.
|
||||
|
||||
- `interface Traversable<TKey, TValue>`
|
||||
- `interface ArrayAccess<TKey, TValue>`
|
||||
- `interface IteratorAggregate<TKey, TValue> extends Traversable<TKey, TValue>`
|
||||
- `interface Iterator<TKey, TValue> extends Traversable<TKey, TValue>`
|
||||
- `interface SeekableIterator<TKey, TValue> extends Iterator<TKey, TValue>`
|
||||
|
||||
- `class Generator<TKey, TValue, TSend, TReturn> extends Traversable<TKey, TValue>`
|
||||
- `class ArrayObject<TKey, TValue> implements IteratorAggregate<TKey, TValue>, ArrayAccess<TKey, TValue>`
|
||||
- `class ArrayIterator<TKey of array-key, TValue> implements SeekableIterator<TKey, TValue>, ArrayAccess<TKey, TValue>`
|
||||
- `class DOMNodeList<TNode of DOMNode> implements Traversable<int, TNode>`
|
||||
- `class SplDoublyLinkedList<TValue> implements Iterator<TKey, TValue>, ArrayAccess<TKey, TValue>`
|
||||
- `class SplQueue<TValue> extends SplDoublyLinkedList<TValue>`
|
||||
|
||||
- `abstract class FilterIterator<TKey, TValue, TIterator>`
|
||||
220
vendor/vimeo/psalm/docs/annotating_code/type_syntax/array_types.md
vendored
Normal file
220
vendor/vimeo/psalm/docs/annotating_code/type_syntax/array_types.md
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
# Array types
|
||||
|
||||
In PHP, the `array` type is commonly used to represent three different data structures:
|
||||
|
||||
[List](https://en.wikipedia.org/wiki/List_(abstract_data_type)):
|
||||
|
||||
```php
|
||||
<?php
|
||||
$a = [1, 2, 3, 4, 5];
|
||||
```
|
||||
|
||||
[Associative array](https://en.wikipedia.org/wiki/Associative_array):
|
||||
|
||||
```php
|
||||
<?php
|
||||
$a = [0 => 'hello', 5 => 'goodbye'];
|
||||
$b = ['a' => 'AA', 'b' => 'BB', 'c' => 'CC']
|
||||
```
|
||||
|
||||
Makeshift [Structs](https://en.wikipedia.org/wiki/Struct_(C_programming_language)):
|
||||
|
||||
```php
|
||||
<?php
|
||||
$a = ['name' => 'Psalm', 'type' => 'tool'];
|
||||
```
|
||||
|
||||
PHP treats all these arrays the same, essentially (though there are some optimisations under the hood for the first case).
|
||||
|
||||
Psalm has a few different ways to represent arrays in its type system:
|
||||
|
||||
## Generic arrays
|
||||
|
||||
Psalm uses a syntax [borrowed from Java](https://en.wikipedia.org/wiki/Generics_in_Java) that allows you to denote the types of both keys *and* values:
|
||||
|
||||
```php
|
||||
/** @return array<TKey, TValue> */
|
||||
```
|
||||
|
||||
You can also specify that an array is non-empty with the special type `non-empty-array<TKey, TValue>`.
|
||||
|
||||
### PHPDoc syntax
|
||||
|
||||
PHPDoc [allows you to specify](https://docs.phpdoc.org/latest/guide/references/phpdoc/types.html#arrays) the type of values a generic array holds with the annotation:
|
||||
|
||||
```php
|
||||
/** @return ValueType[] */
|
||||
```
|
||||
|
||||
In Psalm this annotation is equivalent to `@psalm-return array<array-key, ValueType>`.
|
||||
|
||||
Generic arrays encompass both _associative arrays_ and _lists_.
|
||||
|
||||
## Lists
|
||||
|
||||
(Psalm 3.6+)
|
||||
|
||||
Psalm supports a `list` type that represents continuous, integer-indexed arrays like `["red", "yellow", "blue"]`.
|
||||
|
||||
A frequent way to create a list is with the `$arr[] =` notation.
|
||||
|
||||
These arrays will return true to `array_is_list($arr)`(PHP 8.1+) and represent a large percentage of all array usage in PHP applications.
|
||||
|
||||
A `list` type is of the form `list<SomeType>`, where `SomeType` is any permitted [union type](union_types.md) supported by Psalm.
|
||||
|
||||
- `list` is a subtype of `array<int, mixed>`
|
||||
- `list<Foo>` is a subtype of `array<int, Foo>`.
|
||||
|
||||
List types show their value in a few ways:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @param array<int, string> $arr
|
||||
*/
|
||||
function takesArray(array $arr) : void {
|
||||
if ($arr) {
|
||||
// this index may not be set
|
||||
echo $arr[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param list<string> $arr
|
||||
*/
|
||||
function takesList(array $arr) : void {
|
||||
if ($arr) {
|
||||
// list indexes always start from zero,
|
||||
// so a non-empty list will have an element here
|
||||
echo $arr[0];
|
||||
}
|
||||
}
|
||||
|
||||
takesArray(["hello"]); // this is fine
|
||||
takesArray([1 => "hello"]); // would trigger bug, without warning
|
||||
|
||||
takesList(["hello"]); // this is fine
|
||||
takesList([1 => "hello"]); // triggers warning in Psalm
|
||||
```
|
||||
|
||||
## Array shapes
|
||||
|
||||
Psalm supports a special format for arrays where the key offsets are known: array shapes, also known as "object-like arrays".
|
||||
|
||||
Given an array
|
||||
|
||||
```php
|
||||
<?php
|
||||
["hello", "world", "foo" => new stdClass, 28 => false];
|
||||
```
|
||||
|
||||
Psalm will type it internally as:
|
||||
|
||||
```
|
||||
array{0: string, 1: string, foo: stdClass, 28: false}
|
||||
```
|
||||
|
||||
You can specify types in that format yourself, e.g.
|
||||
|
||||
```php
|
||||
/** @return array{foo: string, bar: int} */
|
||||
```
|
||||
|
||||
Optional keys can be denoted by a trailing `?`, e.g.:
|
||||
|
||||
```php
|
||||
/** @return array{optional?: string, bar: int} */
|
||||
```
|
||||
|
||||
Tip: if you find yourself copying the same complex array shape over and over again to avoid `InvalidArgument` issues, try using [type aliases](utility_types.md#type-aliases), instead.
|
||||
|
||||
### Validating array shapes
|
||||
|
||||
Use [Valinor](https://github.com/CuyZ/Valinor) in strict mode to easily assert array shapes at runtime using Psalm array shape syntax (instead of manually asserting keys with isset):
|
||||
|
||||
```php
|
||||
try {
|
||||
$array = (new \CuyZ\Valinor\MapperBuilder())
|
||||
->mapper()
|
||||
->map(
|
||||
'array{a: string, b: int}',
|
||||
json_decode(file_get_contents('https://.../'), true)
|
||||
);
|
||||
|
||||
/** @psalm-trace $array */; // array{a: string, b: int}
|
||||
|
||||
echo $array['a'];
|
||||
echo $array['b'];
|
||||
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
|
||||
// Do something…
|
||||
}
|
||||
```
|
||||
|
||||
Valinor provides both runtime and static Psalm assertions with full Psalm syntax support and many other features, check out the [Valinor documentation](https://valinor.cuyz.io/latest/) for more info!
|
||||
|
||||
## List shapes
|
||||
|
||||
Starting in Psalm 5, Psalm also supports a special format for list arrays where the key offsets are known.
|
||||
|
||||
Given a list array
|
||||
|
||||
```php
|
||||
<?php
|
||||
["hello", "world", new stdClass, false];
|
||||
```
|
||||
|
||||
Psalm will type it internally as:
|
||||
|
||||
```
|
||||
list{string, string, stdClass, false}
|
||||
```
|
||||
|
||||
You can specify types in that format yourself, e.g.
|
||||
|
||||
```php
|
||||
/** @return list{string, int} */
|
||||
/** @return list{0: string, 1: int} */
|
||||
```
|
||||
|
||||
Optional keys can be denoted by a specifying keys for all elements and specifying a trailing `?` for optional keys, e.g.:
|
||||
|
||||
```php
|
||||
/** @return list{0: string, 1?: int} */
|
||||
```
|
||||
|
||||
List shapes are essentially n-tuples [from a type theory perspective](https://en.wikipedia.org/wiki/Tuple#Type_theory).
|
||||
|
||||
## Unsealed array and list shapes
|
||||
|
||||
Starting from Psalm v5, array shapes and list shapes can be marked as open by adding `...` as their last element.
|
||||
|
||||
Here we have a function `handleOptions` that takes an array of options. The type tells us it has a single known key with type `string`, and potentially many other keys of unknown types.
|
||||
|
||||
```php
|
||||
/** @param array{verbose: string, ...} $options */
|
||||
function handleOptions(array $options): float {
|
||||
if ($options['verbose']) {
|
||||
var_dump($options);
|
||||
}
|
||||
}
|
||||
|
||||
$options = get_opt(/* some code */);
|
||||
$options['verbose'] = isset($options['verbose']);
|
||||
handleOptions($options);
|
||||
```
|
||||
|
||||
## Callable arrays
|
||||
|
||||
An array holding a callable, like PHP's native `call_user_func()` and friends supports it:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$callable = ['myClass', 'aMethod'];
|
||||
$callable = [$object, 'aMethod'];
|
||||
```
|
||||
|
||||
## non-empty-array
|
||||
|
||||
An array which is not allowed to be empty.
|
||||
[Generic syntax](#generic-arrays) is also supported: `non-empty-array<string, int>`.
|
||||
60
vendor/vimeo/psalm/docs/annotating_code/type_syntax/atomic_types.md
vendored
Normal file
60
vendor/vimeo/psalm/docs/annotating_code/type_syntax/atomic_types.md
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# Atomic types
|
||||
|
||||
Atomic types are the basic building block of all type information used in Psalm. Multiple atomic types can be combined, either with [union types](union_types.md) or [intersection types](intersection_types.md). Psalm allows many different sorts of atomic types to be expressed in docblock syntax:
|
||||
|
||||
**Note**: you can view detailed documentation and usage examples for all atomic types by clicking on each type in the following list.
|
||||
|
||||
* [Scalar types](scalar_types.md)
|
||||
* [bool](scalar_types.md#scalar)
|
||||
* [int](scalar_types.md#scalar)
|
||||
* [float](scalar_types.md#scalar)
|
||||
* [string](scalar_types.md#scalar)
|
||||
* [`int-range<x, y>`](scalar_types.md#int-range)
|
||||
* [`int-mask<1, 2, 4>`](scalar_types.md#int-mask1-2-4)
|
||||
* [`int-mask-of<MyClass::CLASS_CONSTANT_*>`](scalar_types.md#int-mask-ofmyclassclass_constant_)
|
||||
* [class-string and class-string<Foo>](scalar_types.md#class-string-interface-string)
|
||||
* [trait-string](scalar_types.md#trait-string)
|
||||
* [enum-string](scalar_types.md#enum-string)
|
||||
* [callable-string](scalar_types.md#callable-string)
|
||||
* [numeric-string](scalar_types.md#numeric-string)
|
||||
* [literal-string](scalar_types.md#literal-string)
|
||||
* [literal-int](scalar_types.md#literal-int)
|
||||
* [array-key](scalar_types.md#array-key)
|
||||
* [numeric](scalar_types.md#numeric)
|
||||
* [scalar](scalar_types.md#scalar)
|
||||
* [Object types](object_types.md)
|
||||
* [object](object_types.md#unnamed-objects)
|
||||
* [object{foo: string}](object_types.md#object-properties)
|
||||
* [Exception, Foo\MyClass and `Foo\MyClass<Bar>`](object_types.md#named-objectsmd)
|
||||
* [Generator](object_types.md#generators)
|
||||
* [Array types](array_types.md)
|
||||
* [array<int, string>](array_types.md#generic-arrays)
|
||||
* [non-empty-array](array_types.md#non-empty-array)
|
||||
* [string\[\]](array_types.md#phpdoc-syntax)
|
||||
* [list & non-empty-list](array_types.md#lists)
|
||||
* [list<string>](array_types.md#lists)
|
||||
* [array{foo: int, bar: string} and list{int, string}](array_types.md#object-like-arrays)
|
||||
* [callable-array](array_types.md#callable-arrays)
|
||||
* [Callable types](callable_types.md)
|
||||
* [Value types](value_types.md)
|
||||
* [null](value_types.md#null)
|
||||
* [true, false](value_types.md#true-false)
|
||||
* [6, 7.0, "forty-two" and 'forty two'](value_types.md#some_string-4-314)
|
||||
* [Foo\Bar::MY_SCALAR_CONST](value_types.md#regular-class-constants)
|
||||
* [Utility types](utility_types.md)
|
||||
* [(T is true ? string : bool)](conditional_types.md)
|
||||
* [`key-of<T>`](utility_types.md#key-oft)
|
||||
* [`value-of<T>`](utility_types.md#value-oft)
|
||||
* [`properties-of<T>`](utility_types.md#properties-oft)
|
||||
* [`class-string-map<T as Foo, T>`](utility_types.md#class-string-mapt-as-foo-t)
|
||||
* [`T[K]`](utility_types.md#tk)
|
||||
* [Type aliases](utility_types.md#type-aliases)
|
||||
* [Variable templates](utility_types.md#variable-templates)
|
||||
* [Other types](other_types.md)
|
||||
* [`iterable<TKey, TValue>`](other_types.md)
|
||||
* [void](other_types.md)
|
||||
* [resource](other_types.md)
|
||||
* [closed-resource](other_types.md)
|
||||
* [Top and bottom types](top_bottom_types.md)
|
||||
* [mixed](top_bottom_types.md#mixed)
|
||||
* [never](top_bottom_types.md#never)
|
||||
58
vendor/vimeo/psalm/docs/annotating_code/type_syntax/callable_types.md
vendored
Normal file
58
vendor/vimeo/psalm/docs/annotating_code/type_syntax/callable_types.md
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# Callable types
|
||||
|
||||
Psalm supports a special format for `callable`s of the form. It can also be used for annotating `Closure`.
|
||||
|
||||
```
|
||||
callable(Type1, OptionalType2=, SpreadType3...):ReturnType
|
||||
```
|
||||
|
||||
Adding `=` after the type implies it is optional, and suffixing with `...` implies the use of the spread operator.
|
||||
|
||||
Using this annotation you can specify that a given function return a `Closure` e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return Closure(bool):int
|
||||
*/
|
||||
function delayedAdd(int $x, int $y) : Closure {
|
||||
return function(bool $debug) use ($x, $y) {
|
||||
if ($debug) echo "got here" . PHP_EOL;
|
||||
return $x + $y;
|
||||
};
|
||||
}
|
||||
|
||||
$adder = delayedAdd(3, 4);
|
||||
echo $adder(true);
|
||||
```
|
||||
|
||||
## Pure callables
|
||||
|
||||
For situations where the `callable` needs to be pure or immutable, the subtypes `pure-callable` and `pure-Closure` are also available.
|
||||
|
||||
This can be useful when the `callable` is used in a function marked with `@psalm-pure` or `@psalm-mutation-free`, for example:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @psalm-immutable */
|
||||
class intList {
|
||||
/** @param list<int> $items */
|
||||
public function __construct(private array $items) {}
|
||||
|
||||
/**
|
||||
* @param pure-callable(int, int): int $callback
|
||||
* @psalm-mutation-free
|
||||
*/
|
||||
public function walk(callable $callback): int {
|
||||
return array_reduce($this->items, $callback, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$list = new intList([1,2,3]);
|
||||
|
||||
// This is ok, as the callable is pure
|
||||
echo $list->walk(fn (int $c, int $v): int => $c + $v);
|
||||
|
||||
// This will cause an InvalidArgument error, as the closure calls an impure function
|
||||
echo $list->walk(fn (int $c, int $v): int => $c + random_int(1, $v));
|
||||
```
|
||||
82
vendor/vimeo/psalm/docs/annotating_code/type_syntax/conditional_types.md
vendored
Normal file
82
vendor/vimeo/psalm/docs/annotating_code/type_syntax/conditional_types.md
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Conditional types
|
||||
|
||||
Psalm supports the equivalent of TypeScript’s [conditional types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types).
|
||||
|
||||
Conditional types have the form:
|
||||
|
||||
`(<template param> is <union type> ? <union type> : <union type>)`
|
||||
|
||||
All conditional types must be wrapped inside brackets e.g. `(...)`
|
||||
|
||||
Conditional types are dependent on [template parameters](../templated_annotations.md), so you can only use them in a function where template parameters are defined.
|
||||
|
||||
## Example application
|
||||
|
||||
Let's suppose we want to make a userland implementation of PHP's numeric addition (but please never do this). You could type this with a conditional return type:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @template T of int|float
|
||||
* @param T $a
|
||||
* @param T $b
|
||||
* @return int|float
|
||||
* @psalm-return (T is int ? int : float)
|
||||
*/
|
||||
function add($a, $b) {
|
||||
return $a + $b;
|
||||
}
|
||||
```
|
||||
|
||||
When figuring out the result of `add($x, $y)` Psalm tries to infer the value `T` for that particular call. When calling `add(1, 2)`, `T` can be trivially inferred as an `int`. Then Psalm takes the provided conditional return type
|
||||
|
||||
`(T is int ? int : float)`
|
||||
|
||||
and substitutes in the known value of `T`, `int`, so that expression becomes
|
||||
|
||||
`(int is int ? int : float)`
|
||||
|
||||
which simplifies to `(true ? int : float)`, which simplifies to `int`.
|
||||
|
||||
Calling `add(1, 2.1)` means `T` would instead be inferred as `int|float`, which means the expression `(T is int ? int : float)` would instead have the substitution
|
||||
|
||||
`(int|float is int ? int : float)`
|
||||
|
||||
The union `int|float` is clearly not an `int`, so the expression is simplified to `(false ? int : float)`, which simplifies to `float`.
|
||||
|
||||
## Nested conditionals
|
||||
|
||||
You can also nest conditionals just as you could ternary expressions:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
const TYPE_STRING = 0;
|
||||
const TYPE_INT = 1;
|
||||
|
||||
/**
|
||||
* @template T of int
|
||||
* @param T $i
|
||||
* @psalm-return (
|
||||
* T is self::TYPE_STRING
|
||||
* ? string
|
||||
* : (T is self::TYPE_INT ? int : bool)
|
||||
* )
|
||||
*/
|
||||
public static function getDifferentType(int $i) {
|
||||
if ($i === self::TYPE_STRING) {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
if ($i === self::TYPE_INT) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
36
vendor/vimeo/psalm/docs/annotating_code/type_syntax/intersection_types.md
vendored
Normal file
36
vendor/vimeo/psalm/docs/annotating_code/type_syntax/intersection_types.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Intersection types
|
||||
|
||||
An annotation of the form `Type1&Type2&Type3` is an _Intersection Type_. Any value must satisfy `Type1`, `Type2` and `Type3` simultaneously. `Type1`, `Type2` and `Type3` are all [atomic types](atomic_types.md).
|
||||
|
||||
For example, after this statement in a PHPUnit test:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$hare = $this->createMock(Hare::class);
|
||||
```
|
||||
|
||||
`$hare` will be an instance of a class that extends `Hare`, and implements `\PHPUnit\Framework\MockObject\MockObject`. So
|
||||
`$hare` is typed as `Hare&\PHPUnit\Framework\MockObject\MockObject`. You can use this syntax whenever a value is
|
||||
required to implement multiple interfaces.
|
||||
|
||||
Another use case is being able to merge object-like arrays:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @psalm-type A=array{a: int}
|
||||
* @psalm-type B=array{b: int}
|
||||
*
|
||||
* @param A $a
|
||||
* @param B $b
|
||||
*
|
||||
* @return A&B
|
||||
*/
|
||||
function foo($a, $b) {
|
||||
return $a + $b;
|
||||
}
|
||||
```
|
||||
|
||||
The returned type will contain the properties of both `A` and `B`. In other words, it will be `{a: int, b: int}`.
|
||||
|
||||
Intersections are only valid for lists of only *object types* and lists of only *object-like arrays*.
|
||||
45
vendor/vimeo/psalm/docs/annotating_code/type_syntax/object_types.md
vendored
Normal file
45
vendor/vimeo/psalm/docs/annotating_code/type_syntax/object_types.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Object types
|
||||
|
||||
## Unnamed objects
|
||||
|
||||
`object` are examples of unnamed object types. This type is also a valid type in PHP.
|
||||
|
||||
## Named objects
|
||||
|
||||
`stdClass`, `Foo`, `Bar\Baz` etc. are examples of named object types. These types are also valid types in PHP.
|
||||
|
||||
## Object properties
|
||||
|
||||
Psalm supports specifying the properties of an object and their expected types, e.g.:
|
||||
|
||||
```php
|
||||
/** @param object{foo: string} $obj */
|
||||
function takesObject(object $obj) : string {
|
||||
return $obj->foo;
|
||||
}
|
||||
|
||||
takesObject((object) ["foo" => "hello"]);
|
||||
```
|
||||
|
||||
Optional properties can be denoted by a trailing `?`, e.g.:
|
||||
|
||||
```php
|
||||
/** @param object{optional?: string} */
|
||||
```
|
||||
|
||||
## Generic object types
|
||||
|
||||
Psalm supports using generic object types like `ArrayObject<int, string>`. Any generic object should be typehinted with appropriate [`@template` tags](../templated_annotations.md).
|
||||
|
||||
## Generators
|
||||
|
||||
Generator types support up to four parameters, e.g. `Generator<int, string, mixed, void>`:
|
||||
|
||||
1. `TKey`, the type of the `yield` key - default: `mixed`
|
||||
2. `TValue`, the type of the `yield` value - default: `mixed`
|
||||
3. `TSend`, the type of the `send()` method's parameter - default: `mixed`
|
||||
4. `TReturn`, the return type of the `getReturn()` method - default: `mixed`
|
||||
|
||||
`Generator<int>` is a shorthand for `Generator<mixed, int, mixed, mixed>`.
|
||||
|
||||
`Generator<int, string>` is a shorthand for `Generator<int, string, mixed, mixed>`.
|
||||
6
vendor/vimeo/psalm/docs/annotating_code/type_syntax/other_types.md
vendored
Normal file
6
vendor/vimeo/psalm/docs/annotating_code/type_syntax/other_types.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Other types
|
||||
|
||||
- `iterable` - represents the [iterable pseudo-type](https://php.net/manual/en/language.types.iterable.php). Like arrays, iterables can have type parameters e.g. `iterable<string, Foo>`.
|
||||
- `void` - can be used in a return type when a function does not return a value.
|
||||
- `resource` represents a [PHP resource](https://www.php.net/manual/en/language.types.resource.php).
|
||||
- `closed-resource` represents a [PHP resource](https://www.php.net/manual/en/language.types.resource.php) that was closed (using `fclose` or another closing function).
|
||||
115
vendor/vimeo/psalm/docs/annotating_code/type_syntax/scalar_types.md
vendored
Normal file
115
vendor/vimeo/psalm/docs/annotating_code/type_syntax/scalar_types.md
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
# Scalar types
|
||||
|
||||
## scalar
|
||||
|
||||
`int`, `bool`, `float`, `string` are examples of scalar types. Scalar types represent scalar values in PHP. These types are also valid types in PHP 7.
|
||||
The type `scalar` is the supertype of all scalar types.
|
||||
|
||||
## int-range
|
||||
|
||||
Integer ranges indicate an integer within a range, specified using generic syntax: `int<x, y>`.
|
||||
`x` and `y` must be integer numbers.
|
||||
`x` can also be `min` to indicate PHP_INT_MIN, and `y` can be `max` to indicate PHP_INT_MAX.
|
||||
|
||||
Examples:
|
||||
|
||||
* `int<-1, 3>`
|
||||
* `int<min, 0>`
|
||||
* `int<1, max>` (equivalent to `positive-int`)
|
||||
* `int<0, max>` (equivalent to `non-negative-int`)
|
||||
* `int<min, -1>` (equivalent to `negative-int`)
|
||||
* `int<min, 0>` (equivalent to `non-positive-int`)
|
||||
* `int<min, max>` (equivalent to `int`)
|
||||
|
||||
## int-mask<1, 2, 4>
|
||||
|
||||
Represents the type that is the result of a bitmask combination of its parameters.
|
||||
`int-mask<1, 2, 4>` corresponds to `0|1|2|3|4|5|6|7`.
|
||||
|
||||
## int-mask-of<MyClass::CLASS_CONSTANT_*>
|
||||
|
||||
Represents the type that is the result of a bitmask combination of its parameters.
|
||||
This is the same concept as [`int-mask`](#int-mask1-2-4) but this type is used with a reference to constants in code: `int-mask-of<MyClass::CLASS_CONSTANT_*>` will correspond to `0|1|2|3|4|5|6|7` if there are three constants called `CLASS_CONSTANT_{A,B,C}` with values 1, 2 and 4.
|
||||
|
||||
## array-key
|
||||
|
||||
`array-key` is the supertype (but not a union) of `int` and `string`.
|
||||
|
||||
## numeric
|
||||
|
||||
`numeric` is a supertype of `int` or `float` and [`numeric-string`](#numeric-string).
|
||||
|
||||
## class-string, interface-string
|
||||
|
||||
Psalm supports a special meta-type for `MyClass::class` constants, `class-string`, which can be used everywhere `string` can.
|
||||
|
||||
For example, given a function with a `string` parameter `$class_name`, you can use the annotation `@param class-string $class_name` to tell Psalm make sure that the function is always called with a `::class` constant in that position:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {}
|
||||
|
||||
/**
|
||||
* @param class-string $s
|
||||
*/
|
||||
function takesClassName(string $s) : void {}
|
||||
```
|
||||
|
||||
`takesClassName("A");` would trigger a `TypeCoercion` issue, whereas `takesClassName(A::class)` is fine.
|
||||
|
||||
You can also parameterize `class-string` with an object name e.g. [`class-string<Foo>`](value_types.md#regular-class-constants). This tells Psalm that any matching type must either be a class string of `Foo` or one of its descendants.
|
||||
|
||||
## trait-string
|
||||
|
||||
Psalm also supports a `trait-string` annotation denoting a trait that exists.
|
||||
|
||||
## enum-string
|
||||
|
||||
Psalm also supports a `enum-string` annotation denote an enum that exists.
|
||||
|
||||
## callable-string
|
||||
|
||||
`callable-string` denotes a string value that has passed an `is_callable` check.
|
||||
|
||||
## numeric-string
|
||||
|
||||
`numeric-string` denotes a string value that has passed an `is_numeric` check.
|
||||
|
||||
## literal-string
|
||||
|
||||
`literal-string` denotes a string value that is entirely composed of strings in your application.
|
||||
|
||||
Examples:
|
||||
|
||||
- `"hello " . "world"`
|
||||
- `"hello " . Person::DEFAULT_NAME`
|
||||
- `implode(', ', ["one", "two"])`
|
||||
- `implode(', ', [1, 2, 3])`
|
||||
- `"hello " . <another literal-string>`
|
||||
|
||||
Strings that don't pass this type check:
|
||||
|
||||
- `file_get_contents("foo.txt")`
|
||||
- `$_GET["foo"]`
|
||||
- `"hello " . $_GET["foo"]`
|
||||
|
||||
## literal-int
|
||||
|
||||
`literal-int` denotes an int value that is entirely composed of literal integers in your application.
|
||||
|
||||
Examples:
|
||||
|
||||
- `12`
|
||||
- `12+42`
|
||||
|
||||
Integers that don't pass this type check:
|
||||
|
||||
- `(int) file_get_contents("foo.txt")`
|
||||
- `(int) $_GET["foo"]`
|
||||
- `((int)$_GET["foo"]) + 2`
|
||||
|
||||
## lowercase-string, non-empty-string, non-empty-lowercase-string
|
||||
|
||||
A non empty string, lowercased or both at once.
|
||||
|
||||
`empty` here is defined as all strings except the empty string `''`. Another type `non-falsy-string` is effectively a subtype of `non-empty-string`, and also precludes the string value `'0'`.
|
||||
17
vendor/vimeo/psalm/docs/annotating_code/type_syntax/top_bottom_types.md
vendored
Normal file
17
vendor/vimeo/psalm/docs/annotating_code/type_syntax/top_bottom_types.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
# Top types, bottom types
|
||||
|
||||
## `mixed`
|
||||
|
||||
This is the _top type_ in PHP's type system, and represents a lack of type information. Psalm warns about `mixed` types when the `reportMixedIssues` flag is turned on, or when you're on level 1.
|
||||
|
||||
## `never`
|
||||
|
||||
It can be aliased to `no-return` or `never-return` in docblocks. Note: it replaced the old `empty` type that used to exist in Psalm
|
||||
|
||||
This is the _bottom type_ in PHP's type system. It's used to describe a type that has no possible value. It can happen in multiple cases:
|
||||
|
||||
- the actual `never` type from PHP 8.1 (can be used in docblocks for older versions). This type can be used as a return type for functions that will never return, either because they always throw exceptions or always exit()
|
||||
- an union type that have been stripped for all its possible types. (For example, if a variable is `string|int` and we perform a is_bool() check in a condition, the type of the variable in the condition will be `never` as the condition will never be entered)
|
||||
- it can represent a placeholder for types yet to come — a good example is the type of the empty array `[]`, which Psalm types as `array<never, never>`, the content of the array is void so it can accept any content
|
||||
- it can also happen in the same context as the line above for templates that have yet to be defined
|
||||
16
vendor/vimeo/psalm/docs/annotating_code/type_syntax/union_types.md
vendored
Normal file
16
vendor/vimeo/psalm/docs/annotating_code/type_syntax/union_types.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Union Types
|
||||
|
||||
An annotation of the form `Type1|Type2|Type3` is a _Union Type_. `Type1`, `Type2` and `Type3` are all acceptable possible types of that union type.
|
||||
|
||||
`Type1`, `Type2` and `Type3` are each [atomic types](atomic_types.md).
|
||||
|
||||
Union types can be generated in a number of different ways, for example in ternary expressions:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$rabbit = rand(0, 10) === 4 ? 'rabbit' : ['rabbit'];
|
||||
```
|
||||
|
||||
`$rabbit` will be either a `string` or an `array`. We can represent that idea with Union Types – so `$rabbit` is typed as `string|array`. Union types represent *all* the possible types a given variable can have.
|
||||
|
||||
PHP builtin functions also have union-type returns - `strpos` can return `false` in some situations, `int` in others. We represent that union type with `int|false`.
|
||||
364
vendor/vimeo/psalm/docs/annotating_code/type_syntax/utility_types.md
vendored
Normal file
364
vendor/vimeo/psalm/docs/annotating_code/type_syntax/utility_types.md
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
# Utility types
|
||||
|
||||
Psalm supports some _magical_ utility types that brings superpower to the PHP type system.
|
||||
|
||||
## key-of<T>
|
||||
|
||||
(Psalm 5.0+)
|
||||
|
||||
The `key-of` utility returns the offset-type for any [array type](array_types.md).
|
||||
|
||||
Some examples:
|
||||
|
||||
- `key-of<Foo\Bar::ARRAY_CONST>` evaluates to offset-type of `ARRAY_CONST` (Psalm 3.3+)
|
||||
- `key-of<list<mixed>>` evaluates to `int`
|
||||
- `key-of<array{a: mixed, b: mixed}|array{c: mixed}>` evaluates to `'a'|'b'|'c'`
|
||||
- `key-of<string[]>` evaluates to `array-key`
|
||||
- `key-of<T>` evaluates to the template param's offset-type (ensure `@template T of array`)
|
||||
|
||||
### Notes on template usage
|
||||
|
||||
If you use `key-of` with a template param, you can fulfill the type check only with these allowed methods:
|
||||
|
||||
- `array_keys($t)`
|
||||
- `array_key_first($t)`
|
||||
- `array_key_last($t)`
|
||||
|
||||
Currently `array_key_exists($key, $t)` **does not** infer that `$key` is of `key-of<T>`.
|
||||
|
||||
```php
|
||||
/**
|
||||
* @template T of array
|
||||
* @param T $array
|
||||
* @return list<key-of<T>>
|
||||
*/
|
||||
function getKeys($array) {
|
||||
return array_keys($array);
|
||||
}
|
||||
```
|
||||
|
||||
## value-of<T>
|
||||
|
||||
(Psalm 5.0+)
|
||||
|
||||
The `value-of` utility returns the value-type for any [array type](array_types.md).
|
||||
|
||||
Some examples:
|
||||
|
||||
- `value-of<Foo\Bar::ARRAY_CONST>` evaluates to value-type of `ARRAY_CONST` (Psalm 3.3+)
|
||||
- `value-of<list<float>>` evaluates to `float`
|
||||
- `value-of<array{a: bool, b: int}|array{c: string}>` evaluates to `bool|int|string`
|
||||
- `value-of<string[]>` evaluates to `string`
|
||||
- `value-of<T>` evaluates to the template param's value-type (ensure `@template T of array`)
|
||||
|
||||
### Use with enumerations
|
||||
|
||||
In addition to array-types, `value-of` can also be used to specify an `int` or `string` that contains one of the possible values of a `BackedEnum`:
|
||||
|
||||
- `value-of<Suit>` evaluates to `'H'|'D'|'C'|'S'` (see [Backed enumerations](https://www.php.net/manual/en/language.enumerations.backed.php))
|
||||
- `value-of<BinaryDigits>` evaluates to `0|1`
|
||||
|
||||
### Notes on template usage
|
||||
|
||||
If you use `value-of` with a template param, you can fulfill the type check only with these allowed methods:
|
||||
|
||||
- `array_values`
|
||||
|
||||
```php
|
||||
/**
|
||||
* @template T of array
|
||||
* @param T $array
|
||||
* @return value-of<T>[]
|
||||
*/
|
||||
function getValues($array) {
|
||||
return array_values($array);
|
||||
}
|
||||
```
|
||||
|
||||
Currently `in_array($value, $t)` **does not** infer that `$value` is of `value-of<T>`.
|
||||
|
||||
## properties-of<T>
|
||||
|
||||
(Psalm 5.0+)
|
||||
|
||||
This collection of _utility types_ construct a keyed-array type, with the names of non-static properties of a class as
|
||||
keys, and their respective types as values. This can be useful if you need to convert objects into arrays.
|
||||
|
||||
```php
|
||||
class A {
|
||||
public string $foo = 'foo!';
|
||||
public int $bar = 42;
|
||||
|
||||
/**
|
||||
* @return properties-of<self>
|
||||
*/
|
||||
public function asArray(): array {
|
||||
return [
|
||||
'foo' => $this->foo,
|
||||
'bar' => $this->bar,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<key-of<properties-of<self>>>
|
||||
*/
|
||||
public function attributeNames(): array {
|
||||
return ['foo', 'bar']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Variants
|
||||
|
||||
Note that `properties-of<T>` will return **all non-static** properties. There are the following subtypes to pick only
|
||||
properties with a certain visibility:
|
||||
|
||||
- `public-properties-of<T>`
|
||||
- `protected-properties-of<T>`
|
||||
- `private-properties-of<T>`
|
||||
|
||||
### Sealed array support
|
||||
|
||||
Use final classes if you want to properties-of and get_object_vars to return sealed arrays:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @template T
|
||||
* @param T $object
|
||||
* @return properties-of<T>
|
||||
*/
|
||||
function asArray($object): array {
|
||||
return get_object_vars($object);
|
||||
}
|
||||
|
||||
|
||||
class A {
|
||||
public string $foo = 'foo!';
|
||||
public int $bar = 42;
|
||||
}
|
||||
|
||||
final class B extends A {
|
||||
public float $baz = 2.1;
|
||||
}
|
||||
|
||||
$a = asArray(new A);
|
||||
/** @psalm-trace $a */; // array{foo: string, bar: int, ...}
|
||||
|
||||
$b = asArray(new B);
|
||||
/** @psalm-trace $b */; // array{foo: string, bar: int, baz: float}
|
||||
```
|
||||
|
||||
## class-string-map<T as Foo, T>
|
||||
|
||||
Used to indicate an array where each value is equal an instance of the class string contained in the key:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Foo {}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Bar extends Foo {}
|
||||
|
||||
class A {
|
||||
/** @var class-string-map<T as Foo, T> */
|
||||
private static array $map = [];
|
||||
|
||||
/**
|
||||
* @template U as Foo
|
||||
* @param class-string<U> $class
|
||||
* @return U
|
||||
*/
|
||||
public static function get(string $class) : Foo {
|
||||
if (isset(self::$map[$class])) {
|
||||
return self::$map[$class];
|
||||
}
|
||||
|
||||
self::$map[$class] = new $class();
|
||||
return self::$map[$class];
|
||||
}
|
||||
}
|
||||
|
||||
$foo = A::get(Foo::class);
|
||||
$bar = A::get(Bar::class);
|
||||
|
||||
/** @psalm-trace $foo */; // Foo
|
||||
/** @psalm-trace $bar */; // Bar
|
||||
```
|
||||
|
||||
If we had used an `array<class-string<Foo>, Foo>` instead of a `class-string-map<T as Foo, T>` in the above example, we would've gotten some false positive `InvalidReturnStatement` issues, caused by the lack of a type assertion inside the `isset`.
|
||||
On the other hand, when using `class-string-map`, Psalm assumes that the value obtained by using a key `class-string<T>` is always equal to `T`.
|
||||
|
||||
Unbounded templates can also be used for unrelated classes:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Foo {}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Bar {}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Baz {}
|
||||
|
||||
class A {
|
||||
/** @var class-string-map<T, T> */
|
||||
private static array $map = [];
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param class-string<U> $class
|
||||
* @return U
|
||||
*/
|
||||
public static function get(string $class) : object {
|
||||
if (isset(self::$map[$class])) {
|
||||
return self::$map[$class];
|
||||
}
|
||||
|
||||
self::$map[$class] = new $class();
|
||||
return self::$map[$class];
|
||||
}
|
||||
}
|
||||
|
||||
$foo = A::get(Foo::class);
|
||||
$bar = A::get(Bar::class);
|
||||
$baz = A::get(Baz::class);
|
||||
|
||||
/** @psalm-trace $foo */; // Foo
|
||||
/** @psalm-trace $bar */; // Bar
|
||||
/** @psalm-trace $baz */; // Baz
|
||||
```
|
||||
|
||||
## `T[K]`
|
||||
|
||||
Used to get the value corresponding to the specified key:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @template T as array
|
||||
* @template TKey as string
|
||||
* @param T $arr
|
||||
* @param TKey $k
|
||||
* @return T[TKey]
|
||||
*/
|
||||
function a(array $arr, string $k): mixed {
|
||||
assert(isset($arr[$k]));
|
||||
return $arr[$k];
|
||||
}
|
||||
|
||||
$a = a(['test' => 123], 'test');
|
||||
/** @psalm-trace $a */; // 123
|
||||
```
|
||||
|
||||
## Type aliases
|
||||
|
||||
Psalm allows defining type aliases for complex types (like array shapes) which must be reused often:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @psalm-type PhoneType = array{phone: string}
|
||||
*/
|
||||
class Phone {
|
||||
/**
|
||||
* @psalm-return PhoneType
|
||||
*/
|
||||
public function toArray(): array {
|
||||
return ["phone" => "Nokia"];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can use the [`@psalm-import-type`](../supported_annotations.md#psalm-import-type) annotation to import a type defined with [`@psalm-type`](../supported_annotations.md#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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Variable templates
|
||||
|
||||
Variable templates allow directly using variables instead of template types, for example instead of the following verbose example:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @template TA as string
|
||||
* @template TB as string
|
||||
* @template TChoose as bool
|
||||
* @param TA $a
|
||||
* @param TB $b
|
||||
* @param TChoose $choose
|
||||
* @return (TChoose is true ? TA : TB)
|
||||
*/
|
||||
function pick(string $a, string $b, bool $choose): string {
|
||||
return $choose ? $a : $b;
|
||||
}
|
||||
|
||||
$a = pick('a', 'b', true);
|
||||
/** @psalm-trace $a */; // 'a'
|
||||
|
||||
$a = pick('a', 'b', false);
|
||||
/** @psalm-trace $a */; // 'b'
|
||||
```
|
||||
|
||||
We can instead use variable templates like so:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @return ($choose is true ? $a : $b)
|
||||
*/
|
||||
function pick(string $a, string $b, bool $choose): string {
|
||||
return $choose ? $a : $b;
|
||||
}
|
||||
|
||||
$a = pick('a', 'b', true);
|
||||
/** @psalm-trace $a */; // 'a'
|
||||
|
||||
$a = pick('a', 'b', false);
|
||||
/** @psalm-trace $a */; // 'b'
|
||||
```
|
||||
48
vendor/vimeo/psalm/docs/annotating_code/type_syntax/value_types.md
vendored
Normal file
48
vendor/vimeo/psalm/docs/annotating_code/type_syntax/value_types.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# Value types
|
||||
|
||||
Psalm also allows you to specify values in types.
|
||||
|
||||
## null
|
||||
|
||||
This is the `null` value, destroyer of worlds. Use it sparingly. Psalm supports you writing `?Foo` to mean `null|Foo`.
|
||||
|
||||
## true, false
|
||||
|
||||
Use of `true` and `false` is also PHPDoc-compatible
|
||||
|
||||
## "some_string", 4, 3.14
|
||||
|
||||
Psalm also allows you specify literal values in types, e.g. `@return "good"|"bad"`
|
||||
|
||||
## Regular class constants
|
||||
|
||||
Psalm allows you to include class constants in types, e.g. `@return Foo::GOOD|Foo::BAD`. You can also specify explicit class strings e.g. `Foo::class|Bar::class`
|
||||
|
||||
If you want to specify that a parameter should only take class strings that are, or extend, a given class, you can use the annotation `@param class-string<Foo> $foo_class`. If you only want the param to accept that exact class string, you can use the annotation `Foo::class`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {}
|
||||
class AChild extends A {}
|
||||
class B {}
|
||||
class BChild extends B {}
|
||||
|
||||
/**
|
||||
* @param class-string<A>|class-string<B> $s
|
||||
*/
|
||||
function foo(string $s) : void {}
|
||||
|
||||
/**
|
||||
* @param A::class|B::class $s
|
||||
*/
|
||||
function bar(string $s) : void {}
|
||||
|
||||
foo(A::class); // works
|
||||
foo(AChild::class); // works
|
||||
foo(B::class); // works
|
||||
foo(BChild::class); // works
|
||||
bar(A::class); // works
|
||||
bar(AChild::class); // fails
|
||||
bar(B::class); // works
|
||||
bar(BChild::class); // fails
|
||||
```
|
||||
173
vendor/vimeo/psalm/docs/annotating_code/typing_in_psalm.md
vendored
Normal file
173
vendor/vimeo/psalm/docs/annotating_code/typing_in_psalm.md
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
# Typing in Psalm
|
||||
|
||||
Psalm is able to interpret all PHPDoc type annotations, and use them to further understand the codebase.
|
||||
|
||||
Types are used to describe acceptable values for properties, variables, function parameters and `return $x`.
|
||||
|
||||
## Docblock Type Syntax
|
||||
|
||||
Psalm allows you to express a lot of complicated type information in docblocks.
|
||||
|
||||
All docblock types are either [atomic types](type_syntax/atomic_types.md), [union types](type_syntax/union_types.md) or [intersection types](type_syntax/intersection_types.md).
|
||||
|
||||
Additionally, Psalm supports PHPDoc’s [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).
|
||||
|
||||
## Property declaration types vs Assignment typehints
|
||||
|
||||
You can use the `/** @var Type */` docblock to annotate both [property declarations](http://php.net/manual/en/language.oop5.properties.php) and to help Psalm understand variable assignment.
|
||||
|
||||
### Property declaration types
|
||||
|
||||
You can specify a particular type for a class property declaration in Psalm by using the `@var` declaration:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @var string|null */
|
||||
public $foo;
|
||||
```
|
||||
|
||||
When checking `$this->foo = $some_variable;`, Psalm will check to see whether `$some_variable` is either `string` or `null` and, if neither, emit an issue.
|
||||
|
||||
If you leave off the property type docblock, Psalm will emit a `MissingPropertyType` issue.
|
||||
|
||||
### Assignment typehints
|
||||
|
||||
Consider the following code:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace YourCode {
|
||||
function bar() : int {
|
||||
$a = \ThirdParty\foo();
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
namespace ThirdParty {
|
||||
function foo() {
|
||||
return mt_rand(0, 100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Psalm does not know what the third-party function `ThirdParty\foo` returns, because the author has not added any return types. If you know that the function returns a given value you can use an assignment typehint like so:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace YourCode {
|
||||
function bar() : int {
|
||||
/** @var int */
|
||||
$a = \ThirdParty\foo();
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
namespace ThirdParty {
|
||||
function foo() {
|
||||
return mt_rand(0, 100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This tells Psalm that `int` is a possible type for `$a`, and allows it to infer that `return $a;` produces an integer.
|
||||
|
||||
Unlike property types, however, assignment typehints are not binding – they can be overridden by a new assignment without Psalm emitting an issue e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @var string|null */
|
||||
$a = foo();
|
||||
$a = 6; // $a is now typed as an int
|
||||
```
|
||||
|
||||
You can also use typehints on specific variables e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/** @var string $a */
|
||||
echo strpos($a, 'hello');
|
||||
```
|
||||
|
||||
This tells Psalm to assume that `$a` is a string (though it will still throw an error if `$a` is undefined).
|
||||
|
||||
## Specifying string/int options (aka enums)
|
||||
|
||||
Psalm allows you to specify a specific set of allowed string/int values for a given function or method.
|
||||
|
||||
Whereas this would cause Psalm to [complain that not all paths return a value](https://getpsalm.org/r/9f6f1ceab6):
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo(string $s) : string {
|
||||
switch ($s) {
|
||||
case 'a':
|
||||
return 'hello';
|
||||
|
||||
case 'b':
|
||||
return 'goodbye';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you specify the param type of `$s` as `'a'|'b'` Psalm will know that all paths return a value:
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @param 'a'|'b' $s
|
||||
*/
|
||||
function foo(string $s) : string {
|
||||
switch ($s) {
|
||||
case 'a':
|
||||
return 'hello';
|
||||
|
||||
case 'b':
|
||||
return 'goodbye';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the values are in class constants, you can use those too:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
const FOO = 'foo';
|
||||
const BAR = 'bar';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param A::FOO | A::BAR $s
|
||||
*/
|
||||
function foo(string $s) : string {
|
||||
switch ($s) {
|
||||
case A::FOO:
|
||||
return 'hello';
|
||||
|
||||
case A::BAR:
|
||||
return 'goodbye';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the class constants share a common prefix, you can specify them all using a wildcard:
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
const STATUS_FOO = 'foo';
|
||||
const STATUS_BAR = 'bar';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param A::STATUS_* $s
|
||||
*/
|
||||
function foo(string $s) : string {
|
||||
switch ($s) {
|
||||
case A::STATUS_FOO:
|
||||
return 'hello';
|
||||
|
||||
default:
|
||||
// any other status
|
||||
return 'goodbye';
|
||||
}
|
||||
}
|
||||
```
|
||||
62
vendor/vimeo/psalm/docs/contributing/adding_issues.md
vendored
Normal file
62
vendor/vimeo/psalm/docs/contributing/adding_issues.md
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# Adding a new issue type
|
||||
|
||||
To add a new issue type there are a number of required steps, listed below.
|
||||
|
||||
## Generating a new shortcode
|
||||
|
||||
Run `bin/max_used_shortcode.php` and note the value it printed (`$max_shortcode`)
|
||||
|
||||
## Create issue class
|
||||
|
||||
Create a class in `Psalm\Issue` namespace like this:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Psalm\Issue;
|
||||
|
||||
final class MyNewIssue extends CodeIssue
|
||||
{
|
||||
public const SHORTCODE = 123;
|
||||
public const ERROR_LEVEL = 2;
|
||||
}
|
||||
```
|
||||
|
||||
For `SHORTCODE` value use `$max_shortcode + 1`. To choose appropriate error level see [Error levels](../running_psalm/error_levels.md).
|
||||
|
||||
There a number of abstract classes you can extend:
|
||||
|
||||
* `CodeIssue` - non specific, default issue. It's a base class for all issues.
|
||||
* `ClassIssue` - issue related to a specific class (also interface, trait, enum). These issues can be suppressed for specific classes in `psalm.xml` by using `referencedClass` attribute
|
||||
* `PropertyIssue` - issue related to a specific property. Can be targeted by using `referencedProperty` in `psalm.xml`
|
||||
* `FunctionIssue` - issue related to a specific function. Can be suppressed with `referencedFunction` attribute.
|
||||
* `ArgumentIssue` - issue related to a specific argument. Can be targeted with `referencedFunction` attribute.
|
||||
* `MethodIssue` - issue related to a specific method. Can be targeted with `referencedMethod` attribute.
|
||||
* `ClassConstantIssue` - issue related ot a specific class constant. Can be targeted with `referencedConstant`.
|
||||
* `VariableIssue` - issue for a specific variable. Targeted with `referencedVariable`
|
||||
|
||||
## Add a `config.xsd` entry
|
||||
|
||||
All issue types needs to be listed in `config.xsd`, which is used to validate `psalm.xml`. Choose appropriate `type` attribute. E.g. for issues extending `PropertyIssue` use `type="PropertyIssueHandlerType"`.
|
||||
|
||||
## Add a doc page for your new issue
|
||||
|
||||
Every issue needs to be documented. Create a markdown file in `docs/running_psalm/issues` folder. Make sure to include a snippet of code illustrating your issue. Important: snippets must use fenced php code block and must include opening PHP tag (`<?php`). The snippet must actually produce the issue you're documenting. It's checked by our test suite.
|
||||
|
||||
## Add links to the doc page
|
||||
|
||||
Add links to the doc page you created to `docs/running_psalm/error_levels.md` and `docs/running_psalm/issues.md`
|
||||
|
||||
## Run documentation tests
|
||||
|
||||
```
|
||||
$ vendor/bin/phpunit tests/DocumentationTest.php
|
||||
```
|
||||
|
||||
It will check whether you did all (or at least most) of the steps above.
|
||||
|
||||
## Use your new issue type in Psalm core
|
||||
|
||||
```php
|
||||
IssueBuffer::maybeAdd(new MyNewIssue(...))
|
||||
```
|
||||
81
vendor/vimeo/psalm/docs/contributing/editing_callmaps.md
vendored
Normal file
81
vendor/vimeo/psalm/docs/contributing/editing_callmaps.md
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
# Altering callmaps
|
||||
|
||||
## Intro
|
||||
|
||||
One of the first things most contributors start with is proposing changes to
|
||||
callmaps.
|
||||
|
||||
Callmap is a data file (formatted as a PHP file returning an array) that tells
|
||||
Psalm what arguments function/method takes and what it returns.
|
||||
|
||||
There are two full callmaps (`CallMap.php` and `CallMap_historical.php`) in
|
||||
`dictionaries` folder, and a number of delta files that provide information on
|
||||
how signatures changed in various PHP versions. `CallMap_historical` has
|
||||
signatures as they were in PHP 7.0, `CallMap.php` contains current signatures
|
||||
(for PHP 8.1 at the time of writing).
|
||||
|
||||
## Full callmap format
|
||||
|
||||
Full callmaps (`CallMap.php` and `CallMap_historical.php`) have function/method
|
||||
names as keys and an array representing the corresponding signature as a value.
|
||||
|
||||
First element of that value is a return type (it also doesn't have a key), and
|
||||
subsequent elements represent function/method parameters. Parameter name for an
|
||||
optional parameter is postfixed with `=`.
|
||||
|
||||
## Delta file format
|
||||
|
||||
Delta files (named `CallMap_<PHP major version><PHP minor version>_delta.php`)
|
||||
list changes that happened in the corresponding PHP version. There are
|
||||
three section with self-explanatory names: `added` (for functions/methods that
|
||||
were added in that PHP version), `removed` (for those that were removed) and
|
||||
`changed`.
|
||||
|
||||
Entry format for `removed` and `added` section matches that of a full callmap,
|
||||
while `changed` entries list `old` and `new` signatures.
|
||||
|
||||
## How Psalm uses delta files
|
||||
|
||||
When the current PHP version is set to something other than the latest PHP
|
||||
version supported by Psalm, it needs to process delta files to arrive at a
|
||||
version of callmap matching the one that is used during analysis. Psalm uses
|
||||
the following process to do that:
|
||||
|
||||
1. Read `CallMap.php` (Note: it's the one having the latest signatures).
|
||||
2. If it matches configured PHP version, use it.
|
||||
3. If the callmap delta for previous PHP version exists, read that.
|
||||
4. Take previous callmap delta and apply it in reverse order. That is, entries
|
||||
in `removed` section are added, those in `added` section are removed and
|
||||
`changed.new` signatures in the current callamp are replaced with
|
||||
`changed.old`.
|
||||
5. Goto 2
|
||||
|
||||
## Consistent histories
|
||||
|
||||
To make sure there are no mismatches in deltas and the callmap, CI validates
|
||||
that all function/method entries have consistent histories. E.g. that the
|
||||
signature in `changed.new` matches the one in `CallMap.php`, the `removed`
|
||||
entries are actually absent from `CallMap.php` and so on.
|
||||
|
||||
## Typical changes
|
||||
|
||||
To put that into practical perspective, let's see how a couple of typical
|
||||
callmap changes may look like.
|
||||
|
||||
### Adding a new function
|
||||
|
||||
Say, there's a function added in PHP 8.1, e.g. `array_is_list()`. Add it to the
|
||||
`CallMap_81_delta.php` (as it was introduced in PHP 8.1), and `CallMap.php` (as
|
||||
it exists in the latest PHP version). Here's [the PR that does it](https://github.com/vimeo/psalm/pull/6398/files).
|
||||
|
||||
### Correcting the function signature
|
||||
|
||||
Assume you found an incorrect signature, the one that was always different to what
|
||||
we currently have in Psalm. This will need a change to `CallMap_historical.php`
|
||||
(as the signature was always that way) and `CallMap.php` (as the signature is
|
||||
still valid). Here's [the PR that does it](https://github.com/vimeo/psalm/pull/6359/files).
|
||||
|
||||
If function signature is correct for an older version but has changed since you
|
||||
will need to edit the delta for PHP version where signature changed and
|
||||
`CallMap.php` (as this new signature is still valid). Here's
|
||||
[the PR that does it (makes `timestamp` nullable)](https://github.com/vimeo/psalm/pull/6244/files).
|
||||
73
vendor/vimeo/psalm/docs/contributing/how_psalm_works.md
vendored
Normal file
73
vendor/vimeo/psalm/docs/contributing/how_psalm_works.md
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# How Psalm works
|
||||
|
||||
The entry point for all analysis is [`ProjectAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php)
|
||||
|
||||
`ProjectAnalyzer` is in charge of two things: Scanning and Analysis
|
||||
|
||||
## Scanning
|
||||
|
||||
For any file or set of files, Psalm needs to determine all the possible dependencies and get their function signatures and constants, so that the analysis phase can be done multi-threaded.
|
||||
|
||||
Scanning happens in `Psalm\Internal\Codebase\Scanner`.
|
||||
|
||||
The first task is to convert a file into a set of [PHP Parser](https://github.com/nikic/PHP-Parser) statements. PHP Parser converts PHP code into an abstract syntax tree that Psalm uses for all its analysis.
|
||||
|
||||
### Deep scanning vs shallow scanning
|
||||
|
||||
Psalm then uses a custom PHP Parser `NodeVisitor` called [`ReflectorVisitor`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php) which has two modes when scanning a given file: a shallow scan, where it just gets function signatures, return types, constants, and inheritance, or a deep scan, where it drills into every function statement to get those dependencies too (e.g. the class names instantiated by a function). It only does a deep scan on files that it knows will be analysed later (so the vast majority of the vendor directory, for example, just gets a shallow scan).
|
||||
|
||||
So, when analysing the `src` directory, Psalm will deep scan the following file:
|
||||
|
||||
src/A.php
|
||||
```php
|
||||
<?php
|
||||
use Vendor\VendorClass;
|
||||
use Vendor\OtherVendorClass;
|
||||
|
||||
class A extends VendorClass
|
||||
{
|
||||
public function foo(OtherVendorClass $c): void {}
|
||||
}
|
||||
```
|
||||
|
||||
And will also deep scan the file belonging to `Vendor\VendorClass`, because it may have to check instantiations of properties at some point.
|
||||
|
||||
It will do a shallow scan of `Vendor\OtherVendorClass` (and any dependents) because all it cares about are the method signatures and return types of the variable `$c`.
|
||||
|
||||
### Finding files from class names
|
||||
|
||||
To figure out the `ClassName` => `src/FileName.php` mapping it uses reflection for project files and the Composer classmap for vendor files.
|
||||
|
||||
### Storing data from scanning
|
||||
|
||||
For each file that `ReflectorVisitor` visits, Psalm creates a [`FileStorage`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Storage/FileStorage.php) instance, along with [`ClassLikeStorage`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Storage/ClassLikeStorage.php) and [`FunctionLikeStorage`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Storage/FunctionLikeStorage.php) instances depending on the file contents.
|
||||
|
||||
Once we have a set of all files and their classes and function signatures, we calculate inheritance for everything in the [`Populator`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Codebase/Populator.php) class and then move onto analysis.
|
||||
|
||||
At the end of the scanning step we have populated all the necessary information in [`ClassLikes`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Codebase/ClassLikes.php), [`Functions`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Codebase/Functions.php) and [`Methods`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Codebase/Methods.php) classes, and created a complete list of `FileStorage` and `ClassLikeStorage` objects (in [`FileStorageProvider`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Provider/FileStorageProvider.php) and [`ClassLikeStorageProvider`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php) respectively) for all classes and files used in our project.
|
||||
|
||||
## Analysis
|
||||
|
||||
We analyse files in [`FileAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/FileAnalyzer.php)
|
||||
|
||||
The `FileAnalyzer` takes a given file and looks for a set of top-level components: classes, traits, interfaces, functions. It can look inside namespaces and extract the classes, interfaces, traits and functions in them as well.
|
||||
|
||||
It delegates the analysis of those components to [`ClassAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/ClassAnalyzer.php), [`InterfaceAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php) and [`FunctionAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php).
|
||||
|
||||
Because it’s the most basic use case for the line-by-line analysis (no class inheritance to worry about), let’s drill down into `FunctionAnalyzer`.
|
||||
|
||||
### Function Analysis
|
||||
|
||||
`FunctionAnalyzer::analyze` is defined in [`FunctionLikeAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php). That method first gets the `FunctionLikeStorage` object for the given function that we created in our scanning step. That `FunctionLikeStorage` object has information about function parameters, which we then feed into a [`Context`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Context.php) object. The `Context` contains all the type information we know about variables & properties (stored in `Context::$vars_in_scope`) and also a whole bunch of other information that can change depending on assignments and assertions.
|
||||
|
||||
Somewhere in `FunctionLikeAnalyzer::analyze` we create a new [`StatementsAnalyzer`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php) and then call its `analyze()` method, passing in a set of PhpParser nodes. `StatementAnalyzer::analyze` passes off to a number of different checkers (`IfAnalyzer`, `ForeachAnalyzer`, `ExpressionAnalyzer` etc.) for more thorough analysis.
|
||||
|
||||
At each line the `Context` object may or may not be manipulated. At branching points (if statements, loops, ternary etc) the `Context` object is cloned and then, at the end of the branch, Psalm figures out how to resolve the changes and update the uncloned `Context` object.
|
||||
|
||||
The `NodeDataProvider` stores a type for each PhpParser node.
|
||||
|
||||
After all the statements have been analysed we gather up all the return types and compare them to the given return type.
|
||||
|
||||
### Type Reconciliation
|
||||
|
||||
While some updates to the `Context` object are straightforward, others are not. Updating the `Context` object in the light of new type information happens in [`Reconciler`](https://github.com/vimeo/psalm/blob/master/src/Psalm/Type/Reconciler.php), which takes an array assertions e.g. `[“$a” => “!null”]` and a list of existing type information e.g. `$a => string|null` and return a set of updated information e.g. `$a => string`
|
||||
40
vendor/vimeo/psalm/docs/contributing/index.md
vendored
Normal file
40
vendor/vimeo/psalm/docs/contributing/index.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# Contributing to Psalm
|
||||
|
||||
Psalm is made possible through the contributions of [hundreds of developers](https://github.com/vimeo/psalm/graphs/contributors).
|
||||
|
||||
Hopefully you can be one of them?
|
||||
|
||||
## Getting started
|
||||
|
||||
[Here’s a rough guide to the codebase](how_psalm_works.md).
|
||||
|
||||
[Here's the philosophy underpinning the Psalm’s development](philosophy.md).
|
||||
|
||||
I've also put together [a list of Psalm’s complexities](what_makes_psalm_complicated.md).
|
||||
|
||||
Are you looking for low-hanging fruit? Here are some [GitHub issues](https://github.com/vimeo/psalm/issues?q=is%3Aissue+is%3Aopen+label%3A%22easy+problems%22) that shouldn't be too difficult to resolve.
|
||||
|
||||
### Don’t be afraid!
|
||||
|
||||
One great thing about working on Psalm is that it’s _very_ hard to introduce any sort of type error in Psalm’s codebase. There are almost 5,000 PHPUnit tests, so the risk of you messing up (without the CI system noticing) is very small.
|
||||
|
||||
### Why static analysis is cool
|
||||
|
||||
Day-to-day PHP programming involves solving concrete problems, but they're rarely very complex. Psalm, on the other hand, attempts to solve a pretty hard collection of problems, which then allows it to detect a ton of bugs in PHP code without actually executing that code.
|
||||
|
||||
There's a lot of interesting theory behind the things Psalm does, too. If you want you can go very deep, though you don't need to know really any theory to improve Psalm.
|
||||
|
||||
Lastly, working to improve static analysis tools will also make you a better PHP developer – it'll help you think more about how values flow through your program.
|
||||
|
||||
### Guides
|
||||
|
||||
* [Editing callmaps](editing_callmaps.md)
|
||||
* [Adding a new issue type](adding_issues.md)
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Before you send a pull request, make sure you follow these guidelines:
|
||||
|
||||
Run integration checks locally: `composer tests`
|
||||
|
||||
If you're adding new features or fixing bugs, don’t forget to add tests!
|
||||
70
vendor/vimeo/psalm/docs/contributing/philosophy.md
vendored
Normal file
70
vendor/vimeo/psalm/docs/contributing/philosophy.md
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
# Philosophy
|
||||
|
||||
This is about why Psalm is the way it is. This is a living document!
|
||||
|
||||
## Psalm is a tool for PHP written in PHP
|
||||
|
||||
### Advantages
|
||||
|
||||
- PHP is fast enough for most use-cases
|
||||
- Writing the tool in PHP guarantees that PHP community members can contribute to it without too much difficulty
|
||||
|
||||
### Drawbacks
|
||||
|
||||
- Psalm is slow in very large monorepos (over 5 million lines of checked-in code).
|
||||
- Psalm’s language server is more limited in what it can provide than comparable compiled-language tools. For example, it's infeasible to find/change all occurrences of a symbol, in all files that use it, as you type.
|
||||
|
||||
### Comparison to other languages & tools
|
||||
|
||||
Many languages have typecheckers/compilers written in the same language. Popular examples include Go, Rust, and TypeScript. Python is a special case where the semi-official typechecker [MyPy](https://github.com/python/mypy) (written in Python) can also [compile to a C Python extension](https://github.com/python/mypy/blame/master/mypyc/README.md#L6-L10), which runs 4x faster than the interpreted equivalent.
|
||||
|
||||
Some interpreted languages have unofficial open-source typecheckers written in faster compiled languages. In all cases a single mid-to-large company is behind each effort, with a small number of contributors not employed by that company:
|
||||
|
||||
- PHP
|
||||
- [NoVerify](https://github.com/VKCOM/noverify) – written in Go. Runs much faster than Psalm (but does not support many modern PHP features)
|
||||
- Ruby
|
||||
- [Sorbet](https://sorbet.org/) - written in C
|
||||
- Python
|
||||
- [Pyre](https://github.com/facebook/pyre-check) - written in OCaml
|
||||
- [Hack](https://github.com/facebook/hhvm) - the typechecker is written in OCaml and Rust
|
||||
|
||||
## Psalm's primary purpose is finding bugs
|
||||
|
||||
Psalm does a lot, but people mainly use it to find potential bugs in their code.
|
||||
|
||||
All other functionality – the language server, security analysis, manipulating/fixing code is a secondary concern.
|
||||
|
||||
## It's designed to be run on syntactically-correct code
|
||||
|
||||
Psalm is almost always run on PHP code that parses a lint check (`php -l <filename>`) – i.e. syntactically-correct code. Psalm is not a replacement for that syntax check.
|
||||
|
||||
Given Psalm is almost always used on syntatically-correct code it should use a parser built for that purpose, and `nikic/php-parser` is the gold-standard.
|
||||
|
||||
Where Psalm needs to run on syntactically-incorrect code (e.g. in language server mode) Psalm should still use the same parser (and work around any issues that it produces).
|
||||
|
||||
## Docblock annotations are better than type-providing plugins
|
||||
|
||||
Psalm offers a plugin API that allows you to tell it what about your program's property types, return types and parameter types.
|
||||
|
||||
Psalm aims to operate in a space with other static analysis tools. The more users rely on plugins, the less chance those other tools have to understand the user's intent.
|
||||
|
||||
Psalm should encourage developers to use docblock annotations rather than type-providing plugins. This was a driving force in the adoption of [Conditional Types](../annotating_code/type_syntax/conditional_types.md) which allowed Psalm to replace some of its own internal type-providing plugins.
|
||||
|
||||
The other benefit to docblock annotations is verifiability – for the most part Psalm is able to verify that docblock annotations are correct, but it cannot provide many assurances when plugins are used.
|
||||
|
||||
This doesn’t mean that plugins as a whole are bad, or that they can’t provide useful types. A great adjacent use of plugins is to provide stubs with Psalm type annotations for libraries that don’t have them. This helps the PHP ecosystem because those stubs may eventually make their way into the project currently being stubbed.
|
||||
|
||||
## Docblock annotations that can be verified are better than those that cannot
|
||||
|
||||
Psalm currently supports a number of function/class docblock annotations that it's unable to verify:
|
||||
|
||||
- `@psalm-assert`, `@psalm-assert-if-true`, `@psalm-assert-if-false`
|
||||
- `@property`, `@method`, `@mixin`
|
||||
|
||||
Whenever new docblock annotations are added, effort should be made to allow Psalm to verify their correctness.
|
||||
|
||||
## In certain circumstances docblock annotations are better than PHP 8 attributes
|
||||
|
||||
For information that's just designed to be consumed by static analysis tools, docblocks are a better home than PHP 8 attributes.
|
||||
|
||||
A rationale is provided in [this article](https://psalm.dev/articles/php-8-attributes).
|
||||
82
vendor/vimeo/psalm/docs/contributing/what_makes_psalm_complicated.md
vendored
Normal file
82
vendor/vimeo/psalm/docs/contributing/what_makes_psalm_complicated.md
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Things that make developing Psalm complicated
|
||||
|
||||
This is a somewhat informal list that might aid others.
|
||||
|
||||
## Type inference
|
||||
|
||||
Type inference is one of the big things Psalm does. It tries to figure out what different PHP elements (function calls, if/for/foreach statements etc.) mean for the data in your code.
|
||||
|
||||
Within type inference there are a number of tricky areas:
|
||||
|
||||
#### Loops
|
||||
|
||||
Loops are hard to reason about - break and continue are a pain. This analysis mainly takes place in `LoopAnalyzer`
|
||||
|
||||
#### Combining types
|
||||
|
||||
There are lots of edge-cases when combining types together, given the many types Psalm supports. Type combining occurs in `TypeCombiner`.
|
||||
|
||||
#### Logical assertions
|
||||
|
||||
What effect do different PHP elements have on user-asserted logic in if conditionals, ternarys etc. This logic is spread between a number of different classes.
|
||||
|
||||
#### Generics & Templated code
|
||||
|
||||
Figuring out how templated code should work (`@template` tags) and how much it should work like it does in other languages (Hack, TypeScript etc.) is tricky. Psalm also supports things like nested templates (`@template T1 of T2`) which makes things trickier
|
||||
|
||||
## Detecting dead code
|
||||
|
||||
Detecting unused variables requires some fun [data-flow analysis](https://psalm.dev/articles/better-unused-variable-detection).
|
||||
|
||||
Detecting unused classes and methods between different runs requires maintaining references to those classes in cache (see below).
|
||||
|
||||
## Supporting the community
|
||||
- **Supporting formal PHPDoc annotations**
|
||||
- **Supporting informal PHPDoc annotations**
|
||||
e.g. `ArrayIterator|string[]` to denote an `ArrayIterator` over strings
|
||||
- **non-Composer projects**
|
||||
e.g. WordPress
|
||||
|
||||
## Making Psalm fast
|
||||
|
||||
#### Parser-based reflection
|
||||
|
||||
Requires scanning everything necessary for analysis
|
||||
|
||||
#### Forking processes** (non-windows)
|
||||
|
||||
Mostly handled by code borrowed from Phan, but can introduce subtle issues, also requires to think about how to make work happen in processes
|
||||
|
||||
#### Caching thing
|
||||
|
||||
see below
|
||||
|
||||
## Cache invalidation
|
||||
|
||||
#### Invalidating analysis results
|
||||
|
||||
Requires tracking what methods/properties are used in what other files, and invalidating those results when linked methods change
|
||||
|
||||
#### Partial parsing
|
||||
|
||||
Reparsing bits of files that have changed, which is hard
|
||||
|
||||
## Language Server Support
|
||||
|
||||
#### Handling temporary file changes
|
||||
|
||||
When files change Psalm figures out what's changed within them to avoid re-analysing things unnecessarily
|
||||
|
||||
#### Dealing with malformed PHP code
|
||||
|
||||
When people write code, it's not always pretty as they write it. A language server needs to deal with that bad code somehow
|
||||
|
||||
## Fixing code with Psalter
|
||||
|
||||
#### Adding/replacing code
|
||||
|
||||
Figuring out what changed, making edits that could have been made by a human
|
||||
|
||||
#### Minimal diffs
|
||||
|
||||
hard to change more than you need
|
||||
615
vendor/vimeo/psalm/docs/manipulating_code/fixing.md
vendored
Normal file
615
vendor/vimeo/psalm/docs/manipulating_code/fixing.md
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
# Fixing Code
|
||||
|
||||
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]
|
||||
```
|
||||
|
||||
## Safety features
|
||||
|
||||
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:
|
||||
|
||||
- To see what changes Psalter will make ahead of time, you can run it with `--dry-run`.
|
||||
- You can target particular versions of PHP via `--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.
|
||||
- it has a `--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.)
|
||||
- using `--allow-backwards-incompatible-changes=false` you can make sure to not create backwards incompatible changes
|
||||
|
||||
|
||||
## Plugins
|
||||
|
||||
You can pass in your own manipulation plugins e.g.
|
||||
```bash
|
||||
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.
|
||||
|
||||
## Supported fixes
|
||||
|
||||
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`
|
||||
|
||||
### MissingReturnType
|
||||
|
||||
Running `vendor/bin/psalter --issues=MissingReturnType --php-version=7.0` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
and running `vendor/bin/psalter --issues=MissingReturnType --php-version=5.6` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function foo() {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
### MissingClosureReturnType
|
||||
|
||||
As above, except for closures
|
||||
|
||||
### InvalidReturnType
|
||||
|
||||
Running `vendor/bin/psalter --issues=InvalidReturnType` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function foo() {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function foo() {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
There's also support for return typehints, so running `vendor/bin/psalter --issues=InvalidReturnType` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : int {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
### InvalidNullableReturnType
|
||||
|
||||
Running `vendor/bin/psalter --issues=InvalidNullableReturnType --php-version=7.1` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : ?string {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
}
|
||||
```
|
||||
|
||||
and running `vendor/bin/psalter --issues=InvalidNullableReturnType --php-version=7.0` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
function foo() {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
}
|
||||
```
|
||||
|
||||
### InvalidFalsableReturnType
|
||||
|
||||
Running `vendor/bin/psalter --issues=InvalidFalsableReturnType` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return rand(0, 1) ? "hello" : false;
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
function foo() {
|
||||
return rand(0, 1) ? "hello" : false;
|
||||
}
|
||||
```
|
||||
|
||||
### MissingParamType
|
||||
|
||||
Running `vendor/bin/psalter --issues=MissingParamType` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
class C {
|
||||
public static function foo($s) : void {
|
||||
echo $s;
|
||||
}
|
||||
}
|
||||
C::foo("hello");
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
class C {
|
||||
/**
|
||||
* @param string $s
|
||||
*/
|
||||
public static function foo($s) : void {
|
||||
echo $s;
|
||||
}
|
||||
}
|
||||
C::foo("hello");
|
||||
```
|
||||
|
||||
### MissingPropertyType
|
||||
|
||||
Running `vendor/bin/psalter --issues=MissingPropertyType` on
|
||||
|
||||
```php
|
||||
<?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
|
||||
<?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];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MismatchingDocblockParamType
|
||||
|
||||
Given
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends A {}
|
||||
class D {}
|
||||
```
|
||||
|
||||
running `vendor/bin/psalter --issues=MismatchingDocblockParamType` on
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @param B|C $first
|
||||
* @param D $second
|
||||
*/
|
||||
function foo(A $first, A $second) : void {}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @param B|C $first
|
||||
* @param A $second
|
||||
*/
|
||||
function foo(A $first, A $second) : void {}
|
||||
```
|
||||
|
||||
### MismatchingDocblockReturnType
|
||||
|
||||
Running `vendor/bin/psalter --issues=MismatchingDocblockReturnType` on
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
### LessSpecificReturnType
|
||||
|
||||
Running `vendor/bin/psalter --issues=LessSpecificReturnType` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : ?string {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
### PossiblyUndefinedVariable
|
||||
|
||||
Running `vendor/bin/psalter --issues=PossiblyUndefinedVariable` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo()
|
||||
{
|
||||
if (rand(0, 1)) {
|
||||
$a = 5;
|
||||
}
|
||||
echo $a;
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo()
|
||||
{
|
||||
$a = null;
|
||||
if (rand(0, 1)) {
|
||||
$a = 5;
|
||||
}
|
||||
echo $a;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### PossiblyUndefinedGlobalVariable
|
||||
|
||||
Running `vendor/bin/psalter --issues=PossiblyUndefinedGlobalVariable` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
if (rand(0, 1)) {
|
||||
$a = 5;
|
||||
}
|
||||
echo $a;
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
$a = null;
|
||||
if (rand(0, 1)) {
|
||||
$a = 5;
|
||||
}
|
||||
echo $a;
|
||||
```
|
||||
|
||||
### UnusedMethod
|
||||
|
||||
This removes private unused methods.
|
||||
|
||||
Running `vendor/bin/psalter --issues=UnusedMethod` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
private function foo() : void {}
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
### PossiblyUnusedMethod
|
||||
|
||||
This removes protected/public unused methods.
|
||||
|
||||
Running `vendor/bin/psalter --issues=PossiblyUnusedMethod` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
protected function foo() : void {}
|
||||
public function bar() : void {}
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
### UnusedProperty
|
||||
|
||||
This removes private unused properties.
|
||||
|
||||
Running `vendor/bin/psalter --issues=UnusedProperty` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
/** @var string */
|
||||
private $foo;
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
### PossiblyUnusedProperty
|
||||
|
||||
This removes protected/public unused properties.
|
||||
|
||||
Running `vendor/bin/psalter --issues=PossiblyUnusedProperty` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
/** @var string */
|
||||
public $foo;
|
||||
|
||||
/** @var string */
|
||||
protected $bar;
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
|
||||
}
|
||||
|
||||
new A();
|
||||
```
|
||||
|
||||
### UnusedVariable
|
||||
|
||||
This removes unused variables.
|
||||
|
||||
Running `vendor/bin/psalter --issues=UnusedVariable` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo(string $s) : void {
|
||||
$a = 5;
|
||||
$b = 6;
|
||||
$c = $b += $a -= intval($s);
|
||||
echo "foo";
|
||||
}
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo(string $s) : void {
|
||||
echo "foo";
|
||||
}
|
||||
```
|
||||
|
||||
### UnnecessaryVarAnnotation
|
||||
|
||||
This removes unused `@var` annotations
|
||||
|
||||
Running `vendor/bin/psalter --issues=UnnecessaryVarAnnotation` on
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
$a = foo();
|
||||
```
|
||||
|
||||
gives
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo() : string {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
$a = foo();
|
||||
```
|
||||
|
||||
### ParamNameMismatch
|
||||
|
||||
This aligns child class param names with their parent.
|
||||
|
||||
Running `vendor/bin/psalter --issues=ParamNameMismatch` on
|
||||
|
||||
```php
|
||||
<?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
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
```
|
||||
45
vendor/vimeo/psalm/docs/manipulating_code/refactoring.md
vendored
Normal file
45
vendor/vimeo/psalm/docs/manipulating_code/refactoring.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Refactoring Code
|
||||
|
||||
Sometimes you want to make big changes to your codebase like moving methods or classes.
|
||||
|
||||
Psalm has a refactoring tool you can access with either `vendor/bin/psalm-refactor` or `vendor/bin/psalm --refactor`, followed by appropriate commands.
|
||||
|
||||
## Moving all classes from one namespace to another
|
||||
|
||||
```
|
||||
vendor/bin/psalm-refactor --move "Ns1\*" --into "Some\Other\Namespace"
|
||||
```
|
||||
|
||||
This moves all classes in `Ns1` (e.g. `Ns1\Foo`, `Ns1\Baz`) into the specified namespace. Files are moved as necessary.
|
||||
|
||||
## Moving classes between namespaces
|
||||
|
||||
```
|
||||
vendor/bin/psalm-refactor --move "Ns1\Foo" --into "Ns2"
|
||||
```
|
||||
|
||||
This tells Psalm to move class `Ns1\Foo` into the namespace `Ns2`, so any reference to `Ns1\Foo` becomes `Ns2\Foo`). Files are moved as necessary.
|
||||
|
||||
## Moving and renaming classes
|
||||
|
||||
```
|
||||
vendor/bin/psalm-refactor --rename "Ns1\Foo" --to "Ns2\Bar\Baz"
|
||||
```
|
||||
|
||||
This tells Psalm to move class `Ns1\Foo` into the namespace `Ns2\Bar` and rename it to `Baz`, so any reference to `Ns1\Foo` becomes `Ns2\Bar\Baz`). Files are moved as necessary.
|
||||
|
||||
## Moving methods between classes
|
||||
|
||||
```
|
||||
vendor/bin/psalm-refactor --move "Ns1\Foo::bar" --into "Ns2\Baz"
|
||||
```
|
||||
|
||||
This tells Psalm to move a method named `bar` inside `Ns1\Foo` into the class `Ns2\Baz`, so any reference to `Ns1\Foo::bar` becomes `Ns2\Baz::bar`). Psalm currently allows you to move static methods between arbitrary classes, and instance methods into child classes of that instance.
|
||||
|
||||
## Moving and renaming methods
|
||||
|
||||
```
|
||||
vendor/bin/psalm-refactor --rename "Ns1\Foo::bar" --to "Ns2\Baz::bat"
|
||||
```
|
||||
|
||||
This tells Psalm to move method `Ns1\Foo::bar` into the class `Ns2\Baz` and rename it to `bat`, so any reference to `Ns1\Foo::bar` becomes `Ns2\Baz::bat`).
|
||||
43
vendor/vimeo/psalm/docs/running_psalm/checking_non_php_files.md
vendored
Normal file
43
vendor/vimeo/psalm/docs/running_psalm/checking_non_php_files.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Checking non-PHP files
|
||||
|
||||
Psalm supports the ability to check various PHPish files by extending the `FileChecker` class. For example, if you have a template where the variables are set elsewhere, Psalm can scrape those variables and check the template with those variables pre-populated.
|
||||
|
||||
An example TemplateChecker is provided [here](https://github.com/vimeo/psalm/blob/master/examples/TemplateChecker.php).
|
||||
|
||||
## Using `psalm.xml`
|
||||
|
||||
To ensure your custom `FileChecker` is used, you must update the Psalm `fileExtensions` config in psalm.xml:
|
||||
```xml
|
||||
<fileExtensions>
|
||||
<extension name=".php" />
|
||||
<extension name=".phpt" checker="path/to/TemplateChecker.php" />
|
||||
</fileExtensions>
|
||||
```
|
||||
|
||||
## Using custom plugin
|
||||
|
||||
Plugins can register their own custom scanner and analyzer implementations for particular file extensions.
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Psalm\Example;
|
||||
|
||||
use Psalm\Plugin\PluginEntryPointInterface;
|
||||
use Psalm\Plugin\PluginFileExtensionsInterface;
|
||||
use Psalm\Plugin\FileExtensionsInterface;
|
||||
use Psalm\Plugin\RegistrationInterface;
|
||||
|
||||
class CustomPlugin implements PluginEntryPointInterface, PluginFileExtensionsInterface
|
||||
{
|
||||
public function __invoke(RegistrationInterface $registration, ?\SimpleXMLElement $config = null): void
|
||||
{
|
||||
// ... regular plugin processes, stub registration, hook registration
|
||||
}
|
||||
|
||||
public function processFileExtensions(FileExtensionsInterface $fileExtensions, ?SimpleXMLElement $config = null): void
|
||||
{
|
||||
$fileExtensions->addFileTypeScanner('phpt', TemplateScanner::class);
|
||||
$fileExtensions->addFileTypeAnalyzer('phpt', TemplateAnalyzer::class);
|
||||
}
|
||||
}
|
||||
```
|
||||
47
vendor/vimeo/psalm/docs/running_psalm/command_line_usage.md
vendored
Normal file
47
vendor/vimeo/psalm/docs/running_psalm/command_line_usage.md
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# Running Psalm
|
||||
|
||||
Once you've set up your config file, you can run Psalm from your project's root directory with
|
||||
```bash
|
||||
./vendor/bin/psalm
|
||||
```
|
||||
|
||||
and Psalm will scan all files in the project referenced by `<projectFiles>`.
|
||||
|
||||
If you want to run on specific files, use
|
||||
```bash
|
||||
./vendor/bin/psalm file1.php [file2.php...]
|
||||
```
|
||||
|
||||
## Command-line options
|
||||
|
||||
Run with `--help` to see a list of options that Psalm supports.
|
||||
|
||||
## Exit status
|
||||
|
||||
Psalm exits with status `0` when it successfully completed and found no issues,
|
||||
`1` when there was a problem running Psalm and `2` when it completed
|
||||
successfully but found some issues. Any exit status apart from those indicate
|
||||
some internal problem.
|
||||
|
||||
## Shepherd
|
||||
|
||||
Psalm currently offers some GitHub integration with public projects.
|
||||
|
||||
Add `--shepherd` to send information about your build to https://shepherd.dev.
|
||||
|
||||
Currently, Shepherd tracks type coverage (the percentage of types Psalm can infer) on `master` branches.
|
||||
|
||||
## Running Psalm faster
|
||||
|
||||
Psalm has a couple of command-line options that will result in faster builds:
|
||||
|
||||
- `--threads=[n]` to run Psalm’s analysis in a number of threads
|
||||
- `--diff` which only checks files you’ve updated since the last run (and their dependents).
|
||||
|
||||
In Psalm 4 `--diff` is turned on by default (you can disable it with `--no-diff`).
|
||||
|
||||
Data from the last run is stored in the *cache directory*, which may be set in [configuration](./configuration.md).
|
||||
If you are running Psalm on a build server, you may want to configure the server to ensure that the cache directory
|
||||
is preserved between runs.
|
||||
|
||||
Running them together (e.g. `--threads=8 --diff`) will result in the fastest possible Psalm run.
|
||||
621
vendor/vimeo/psalm/docs/running_psalm/configuration.md
vendored
Normal file
621
vendor/vimeo/psalm/docs/running_psalm/configuration.md
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
# Configuration
|
||||
|
||||
Psalm uses an XML config file (by default, `psalm.xml`). A barebones example looks like this:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
</projectFiles>
|
||||
</psalm>
|
||||
```
|
||||
|
||||
Configuration file may be split into several files using [XInclude](https://www.w3.org/TR/xinclude/) tags (c.f. previous example):
|
||||
#### psalm.xml
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
>
|
||||
<xi:include href="files.xml"/>
|
||||
</psalm>
|
||||
```
|
||||
#### files.xml
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectFiles xmlns="https://getpsalm.org/schema/config">
|
||||
<file name="Bar.php" />
|
||||
<file name="Bat.php" />
|
||||
</projectFiles>
|
||||
```
|
||||
|
||||
|
||||
## Optional <psalm /> attributes
|
||||
|
||||
### Coding style
|
||||
|
||||
#### errorLevel
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
errorLevel="[int]"
|
||||
/>
|
||||
```
|
||||
This corresponds to Psalm‘s [error-detection level](error_levels.md).
|
||||
|
||||
#### reportMixedIssues
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
reportMixedIssues="[bool]"
|
||||
/>
|
||||
```
|
||||
Setting this to `"false"` hides all issues with `Mixed` types in Psalm’s output. If not given, this defaults to `"false"` when `errorLevel` is 3 or higher, and `"true"` when the error level is 1 or 2.
|
||||
|
||||
#### totallyTyped
|
||||
|
||||
\(Deprecated\) This setting has been replaced by `reportMixedIssues` which is automatically enabled when `errorLevel` is 1.
|
||||
|
||||
#### resolveFromConfigFile
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
resolveFromConfigFile="[bool]"
|
||||
/>
|
||||
```
|
||||
If this is enabled, relative directories mentioned in the config file will be resolved relative to the location
|
||||
of the config file. If it is disabled, or absent they will be resolved relative to the working directory of the Psalm process.
|
||||
|
||||
New versions of Psalm enable this option when generating config files. Older versions did not include it.
|
||||
|
||||
#### useDocblockTypes
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
useDocblockTypes="[bool]"
|
||||
>
|
||||
```
|
||||
Whether or not to use types as defined in docblocks. Defaults to `true`.
|
||||
|
||||
#### useDocblockPropertyTypes
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
useDocblockPropertyTypes="[bool]"
|
||||
>
|
||||
```
|
||||
If not using all docblock types, you can still use docblock property types. Defaults to `false` (though only relevant if `useDocblockTypes` is `false`).
|
||||
|
||||
#### usePhpDocMethodsWithoutMagicCall
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
usePhpDocMethodsWithoutMagicCall="[bool]"
|
||||
>
|
||||
```
|
||||
The PHPDoc `@method` annotation normally only applies to classes with a `__call` method. Setting this to `true` allows you to use the `@method` annotation to override inherited method return types. Defaults to `false`.
|
||||
|
||||
#### usePhpDocPropertiesWithoutMagicCall
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
usePhpDocPropertiesWithoutMagicCall="[bool]"
|
||||
>
|
||||
```
|
||||
The PHPDoc `@property`, `@property-read` and `@property-write` annotations normally only apply to classes with `__get`/`__set` methods. Setting this to `true` allows you to use the `@property`, `@property-read` and `@property-write` annotations to override property existence checks and resulting property types. Defaults to `false`.
|
||||
|
||||
#### disableVarParsing
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
disableVarParsing="[bool]"
|
||||
/>
|
||||
```
|
||||
|
||||
Disables parsing of `@var` PHPDocs everywhere except for properties. Setting this to `true` can remove many false positives due to outdated `@var` annotations, used before integrations of Psalm generics and proper typing, enforcing Single Source Of Truth principles. Defaults to `false`.
|
||||
|
||||
#### strictBinaryOperands
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
strictBinaryOperands="[bool]"
|
||||
>
|
||||
```
|
||||
If true we force strict typing on numerical and string operations (see https://github.com/vimeo/psalm/issues/24). Defaults to `false`.
|
||||
|
||||
#### rememberPropertyAssignmentsAfterCall
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
rememberPropertyAssignmentsAfterCall="[bool]"
|
||||
>
|
||||
```
|
||||
Setting this to `false` means that any function calls will cause Psalm to forget anything it knew about object properties within the scope of the function it's currently analysing. This duplicates functionality that Hack has. Defaults to `true`.
|
||||
|
||||
#### allowStringToStandInForClass
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
allowStringToStandInForClass="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, strings can be used as classes, meaning `$some_string::someMethod()` is allowed. If `false`, only class constant strings (of the form `Foo\Bar::class`) can stand in for classes, otherwise an `InvalidStringClass` issue is emitted. Defaults to `false`.
|
||||
|
||||
#### disableSuppressAll
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
disableSuppressAll="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, disables wildcard suppression of all issues with `@psalm-suppress all`. Defaults to `false`.
|
||||
|
||||
#### memoizeMethodCallResults
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
memoizeMethodCallResults="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, the results of method calls without arguments passed are remembered between repeated calls of that method on a given object. Defaults to `false`.
|
||||
|
||||
#### hoistConstants
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
hoistConstants="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, constants defined in a function in a file are assumed to be available when requiring that file, and not just when calling that function. Defaults to `false` (i.e. constants defined in functions will *only* be available for use when that function is called)
|
||||
|
||||
#### addParamDefaultToDocblockType
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
addParamDefaultToDocblockType="[bool]"
|
||||
>
|
||||
```
|
||||
Occasionally a param default will not match up with the docblock type. By default, Psalm emits an issue. Setting this flag to `true` causes it to expand the param type to include the param default. Defaults to `false`.
|
||||
|
||||
#### checkForThrowsDocblock
|
||||
```xml
|
||||
<psalm
|
||||
checkForThrowsDocblock="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will check that the developer has supplied `@throws` docblocks for every exception thrown in a given function or method. Defaults to `false`.
|
||||
|
||||
#### checkForThrowsInGlobalScope
|
||||
```xml
|
||||
<psalm
|
||||
checkForThrowsInGlobalScope="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will check that the developer has caught every exception in global scope. Defaults to `false`.
|
||||
|
||||
#### ignoreInternalFunctionFalseReturn
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
ignoreInternalFunctionFalseReturn="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm ignores possibly-false issues stemming from return values of internal functions (like `preg_split`) that may return false, but do so rarely. Defaults to `true`.
|
||||
|
||||
#### ignoreInternalFunctionNullReturn
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
ignoreInternalFunctionNullReturn="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm ignores possibly-null issues stemming from return values of internal array functions (like `current`) that may return null, but do so rarely. Defaults to `true`.
|
||||
|
||||
#### inferPropertyTypesFromConstructor
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
inferPropertyTypesFromConstructor="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm infers property types from assignments seen in straightforward constructors. Defaults to `true`.
|
||||
|
||||
#### findUnusedVariablesAndParams
|
||||
```xml
|
||||
<psalm
|
||||
findUnusedVariablesAndParams="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will attempt to find all unused variables, the equivalent of running with `--find-unused-variables`. Defaults to `false`.
|
||||
|
||||
#### findUnusedCode
|
||||
```xml
|
||||
<psalm
|
||||
findUnusedCode="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will attempt to find all unused code (including unused variables), the equivalent of running with `--find-unused-code`. Defaults to `false`.
|
||||
|
||||
#### findUnusedPsalmSuppress
|
||||
```xml
|
||||
<psalm
|
||||
findUnusedPsalmSuppress="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will report all `@psalm-suppress` annotations that aren't used, the equivalent of running with `--find-unused-psalm-suppress`. Defaults to `false`.
|
||||
|
||||
#### ensureArrayStringOffsetsExist
|
||||
```xml
|
||||
<psalm
|
||||
ensureArrayStringOffsetsExist="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will complain when referencing an explicit string offset on an array e.g. `$arr['foo']` without a user first asserting that it exists (either via an `isset` check or via an object-like array). Defaults to `false`.
|
||||
|
||||
#### ensureArrayIntOffsetsExist
|
||||
```xml
|
||||
<psalm
|
||||
ensureArrayIntOffsetsExist="[bool]"
|
||||
>
|
||||
```
|
||||
When `true`, Psalm will complain when referencing an explicit integer offset on an array e.g. `$arr[7]` without a user first asserting that it exists (either via an `isset` check or via an object-like array). Defaults to `false`.
|
||||
|
||||
#### phpVersion
|
||||
```xml
|
||||
<psalm
|
||||
phpVersion="[string]"
|
||||
>
|
||||
```
|
||||
Set the php version Psalm should assume when checking and/or fixing the project. If this attribute is not set, Psalm uses the declaration in `composer.json` if one is present. It will check against the earliest version of PHP that satisfies the declared `php` dependency
|
||||
|
||||
This can be overridden on the command-line using the `--php-version=` flag which takes the highest precedence over both the `phpVersion` setting and the version derived from `composer.json`.
|
||||
|
||||
#### skipChecksOnUnresolvableIncludes
|
||||
```xml
|
||||
<psalm
|
||||
skipChecksOnUnresolvableIncludes="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `true`, Psalm will skip checking classes, variables and functions after it comes across an `include` or `require` it cannot resolve. This allows code to reference functions and classes unknown to Psalm.
|
||||
|
||||
This defaults to `false`.
|
||||
|
||||
#### sealAllMethods
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
sealAllMethods="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `true`, Psalm will treat all classes as if they had sealed methods, meaning that if you implement the magic method `__call`, you also have to add `@method` for each magic method. Defaults to false.
|
||||
|
||||
#### sealAllProperties
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
sealAllProperties="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `true`, Psalm will treat all classes as if they had sealed properties, meaning that Psalm will disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations and not explicitly defined as a `property`. Defaults to false.
|
||||
|
||||
#### runTaintAnalysis
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
runTaintAnalysis="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `true`, Psalm will run [Taint Analysis](../security_analysis/index.md) on your codebase. This config is the same as if you were running Psalm with `--taint-analysis`.
|
||||
|
||||
#### reportInfo
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
reportInfo="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `false`, Psalm will not consider issue at lower level than `errorLevel` as `info` (they will be suppressed instead). This can be a big improvement in analysis time for big projects. However, this config will prevent Psalm to count or suggest fixes for suppressed issue
|
||||
|
||||
#### allowNamedArgumentCalls
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
allowNamedArgumentCalls="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `false`, Psalm will not report `ParamNameMismatch` issues in your code anymore. This does not replace the use of individual `@no-named-arguments` to prevent external access to a library's method or to reduce the type to a `list` when using variadics
|
||||
|
||||
#### triggerErrorExits
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
triggerErrorExits="[string]"
|
||||
>
|
||||
```
|
||||
|
||||
Describe the behavior of trigger_error. `always` means it always exits, `never` means it never exits, `default` means it exits only for `E_USER_ERROR`. Default is `default`
|
||||
|
||||
### Running Psalm
|
||||
|
||||
#### autoloader
|
||||
```xml
|
||||
<psalm
|
||||
autoloader="[string]"
|
||||
>
|
||||
```
|
||||
If your application registers one or more custom autoloaders, and/or declares universal constants/functions, this autoloader script will be executed by Psalm before scanning starts. Psalm always registers composer's autoloader by default.
|
||||
|
||||
#### throwExceptionOnError
|
||||
```xml
|
||||
<psalm
|
||||
throwExceptionOnError="[bool]"
|
||||
>
|
||||
```
|
||||
Useful in testing, this makes Psalm throw a regular-old exception when it encounters an error. Defaults to `false`.
|
||||
|
||||
#### hideExternalErrors
|
||||
```xml
|
||||
<psalm
|
||||
hideExternalErrors="[bool]"
|
||||
>
|
||||
```
|
||||
Whether or not to show issues in files that are used by your project files, but which are not included in `<projectFiles>`. Defaults to `false`.
|
||||
|
||||
#### hideAllErrorsExceptPassedFiles
|
||||
```xml
|
||||
<psalm
|
||||
hideAllErrorsExceptPassedFiles="[bool]"
|
||||
>
|
||||
```
|
||||
Whether or not to report issues only for files that were passed explicitly as arguments in CLI. This means any files that are loaded with require/include will not report either, if not set in CLI. Useful if you want to only check errors in a single or selected files. Defaults to `false`.
|
||||
|
||||
#### cacheDirectory
|
||||
```xml
|
||||
<psalm
|
||||
cacheDirectory="[string]"
|
||||
>
|
||||
```
|
||||
The directory used to store Psalm's cache data - if you specify one (and it does not already exist), its parent directory must already exist, otherwise Psalm will throw an error.
|
||||
|
||||
Defaults to `$XDG_CACHE_HOME/psalm`. If `$XDG_CACHE_HOME` is either not set or empty, a default equal to `$HOME/.cache/psalm` is used or `sys_get_temp_dir() . '/psalm'` when not defined.
|
||||
|
||||
#### allowFileIncludes
|
||||
```xml
|
||||
<psalm
|
||||
allowFileIncludes="[bool]"
|
||||
>
|
||||
```
|
||||
Whether or not to allow `require`/`include` calls in your PHP. Defaults to `true`.
|
||||
|
||||
#### serializer
|
||||
```xml
|
||||
<psalm
|
||||
serializer="['igbinary'|'default']"
|
||||
>
|
||||
```
|
||||
Allows you to hard-code a serializer for Psalm to use when caching data. By default, Psalm uses `ext-igbinary` *if* the version is greater than or equal to 2.0.5, otherwise it defaults to PHP's built-in serializer.
|
||||
|
||||
#### threads
|
||||
```xml
|
||||
<psalm
|
||||
threads="[int]"
|
||||
>
|
||||
```
|
||||
Allows you to hard-code the number of threads Psalm will use (similar to `--threads` on the command line). This value will be used in place of detecting threads from the host machine, but will be overridden by using `--threads` or `--debug` (which sets threads to 1) on the command line
|
||||
|
||||
#### maxStringLength
|
||||
```xml
|
||||
<psalm
|
||||
maxStringLength="1000"
|
||||
>
|
||||
```
|
||||
This setting controls the maximum length of literal strings that will be transformed into a literal string type during Psalm analysis.
|
||||
Strings longer than this value (by default 1000 bytes) will be transformed in a generic `non-empty-string` type, instead.
|
||||
|
||||
Please note that changing this setting might introduce unwanted side effects and those side effects won't be considered as bugs.
|
||||
|
||||
#### maxShapedArraySize
|
||||
```xml
|
||||
<psalm
|
||||
maxShapedArraySize="100"
|
||||
>
|
||||
```
|
||||
This setting controls the maximum size of shaped arrays that will be transformed into a shaped `array{key1: "value", key2: T}` type during Psalm analysis.
|
||||
Arrays bigger than this value (100 by default) will be transformed in a generic `non-empty-array` type, instead.
|
||||
|
||||
Please note that changing this setting might introduce unwanted side effects and those side effects won't be considered as bugs.
|
||||
|
||||
#### restrictReturnTypes
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
restrictReturnTypes="true"
|
||||
>
|
||||
```
|
||||
|
||||
Emits `LessSpecificReturnType` when the declared return type is not as tight as
|
||||
the inferred return type.
|
||||
|
||||
This code:
|
||||
```php
|
||||
function getOne(): int // declared type: int
|
||||
{
|
||||
return 1; // inferred type: 1 (int literal)
|
||||
}
|
||||
```
|
||||
Will give this error: `LessSpecificReturnType - The inferred return type '1' for
|
||||
a is more specific than the declared return type 'int'`
|
||||
|
||||
To fix the error, you should specify the more specific type in the doc-block:
|
||||
```php
|
||||
/**
|
||||
* @return 1
|
||||
*/
|
||||
function getOne(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**Warning**: Forcing a tighter type is not always the best course of action and
|
||||
may cause unexpected results. The following code is invalid with
|
||||
`restrictReturnTypes="true"`:
|
||||
```php
|
||||
class StandardCar {
|
||||
/**
|
||||
* @return list{'motor', 'brakes', 'wheels'}
|
||||
*/
|
||||
public function getSystems(): array {
|
||||
return ['motor', 'brakes', 'wheels'];
|
||||
}
|
||||
}
|
||||
|
||||
class PremiumCar extends StandardCar {
|
||||
/**
|
||||
* @return list{'motor', 'brakes', 'wheels', 'rear parking sensor'}
|
||||
*/
|
||||
public function getSystems(): array {
|
||||
return ['motor', 'brakes', 'wheels', 'rear parking sensor'];
|
||||
}
|
||||
}
|
||||
```
|
||||
`ImplementedReturnTypeMismatch - The inherited return type 'list{'motor', 'brakes', 'wheels'}' for StandardCar::getSystems is different to the implemented return type for PremiumCar::getsystems 'list{'motor', 'brakes', 'wheels', 'rear parking sensor'}'`
|
||||
|
||||
#### findUnusedBaselineEntry
|
||||
|
||||
Emits [UnusedBaselineEntry](issues/UnusedBaselineEntry.md) when a baseline entry
|
||||
is not being used to suppress an issue.
|
||||
|
||||
## Project settings
|
||||
|
||||
#### <projectFiles>
|
||||
Contains a list of all the directories that Psalm should inspect. You can also specify a set of files and folders to ignore with the `<ignoreFiles>` directive. By default, ignored files/folders are required to exist. An `allowMissingFiles` attribute can be added for ignored files/folders than may or may not exist.
|
||||
```xml
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="src/Stubs" />
|
||||
</ignoreFiles>
|
||||
<ignoreFiles allowMissingFiles="true">
|
||||
<directory name="path-that-may-not-exist" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
```
|
||||
|
||||
#### <extraFiles>
|
||||
Optional. Same format as `<projectFiles>`. Directories Psalm should load but not inspect.
|
||||
|
||||
#### <fileExtensions>
|
||||
Optional. A list of extensions to search over. See [Checking non-PHP files](checking_non_php_files.md) to understand how to extend this.
|
||||
|
||||
#### <enableExtensions>
|
||||
Optional. A list of extensions to enable. By default, only extensions required by your composer.json will be enabled.
|
||||
```xml
|
||||
<enableExtensions>
|
||||
<extension name="decimal"/>
|
||||
<extension name="pdo"/>
|
||||
</enableExtensions>
|
||||
```
|
||||
|
||||
#### <disableExtensions>
|
||||
Optional. A list of extensions to disable. By default, only extensions required by your composer.json will be enabled.
|
||||
```xml
|
||||
<disableExtensions>
|
||||
<extension name="gmp"/>
|
||||
</disableExtensions>
|
||||
```
|
||||
|
||||
#### <plugins>
|
||||
Optional. A list of `<plugin filename="path_to_plugin.php" />` entries. See the [Plugins](plugins/using_plugins.md) section for more information.
|
||||
|
||||
#### <issueHandlers>
|
||||
Optional. If you don't want Psalm to complain about every single issue it finds, the issueHandler tag allows you to configure that. [Dealing with code issues](dealing_with_code_issues.md) tells you more.
|
||||
|
||||
#### <mockClasses>
|
||||
Optional. Do you use mock classes in your tests? If you want Psalm to ignore them when checking files, include a fully-qualified path to the class with `<class name="Your\Namespace\ClassName" />`
|
||||
|
||||
#### <universalObjectCrates>
|
||||
Optional. Do you have objects with properties that cannot be determined statically? If you want Psalm to treat all properties on a given classlike as mixed, include a fully-qualified path to the class with `<class name="Your\Namespace\ClassName" />`. By default, `stdClass` and `SimpleXMLElement` are configured to be universal object crates.
|
||||
|
||||
#### <stubs>
|
||||
Optional. If your codebase uses classes and functions that are not visible to Psalm via reflection
|
||||
(e.g. if there are internal packages that your codebase relies on that are not available on the machine running Psalm),
|
||||
you can use stub files. Used by PhpStorm (a popular IDE) and others, stubs provide a description of classes and
|
||||
functions without the implementations.
|
||||
|
||||
You can find a list of stubs for common classes [here](https://github.com/JetBrains/phpstorm-stubs).
|
||||
List out each file with `<file name="path/to/file.php" />`. In case classes to be tested use parent classes
|
||||
or interfaces defined in a stub file, this stub should be configured with attribute `preloadClasses="true"`.
|
||||
|
||||
```xml
|
||||
<stubs>
|
||||
<file name="path/to/file.php" />
|
||||
<file name="path/to/abstract-class.php" preloadClasses="true" />
|
||||
</stubs>
|
||||
```
|
||||
|
||||
#### <ignoreExceptions>
|
||||
Optional. A list of exceptions to not report for `checkForThrowsDocblock` or `checkForThrowsInGlobalScope`. If an exception has `onlyGlobalScope` set to `true`, only `checkForThrowsInGlobalScope` is ignored for that exception, e.g.
|
||||
```xml
|
||||
<ignoreExceptions>
|
||||
<class name="fully\qualified\path\Exc" onlyGlobalScope="true" />
|
||||
</ignoreExceptions>
|
||||
```
|
||||
|
||||
#### <globals>
|
||||
Optional. If your codebase uses global variables that are accessed with the `global` keyword, you can declare their type. e.g.
|
||||
```xml
|
||||
<globals>
|
||||
<var name="globalVariableName" type="type" />
|
||||
</globals>
|
||||
```
|
||||
|
||||
Some frameworks and libraries expose functionalities through e.g. `$GLOBALS[DB]->query($query)`.
|
||||
The following configuration declares custom types for super-globals (`$GLOBALS`, `$_GET`, ...).
|
||||
|
||||
```xml
|
||||
<globals>
|
||||
<var name="GLOBALS" type="array{DB: MyVendor\DatabaseConnection, VIEW: MyVendor\TemplateView}" />
|
||||
<var name="_GET" type="array{data: array<string, string>}" />
|
||||
</globals>
|
||||
```
|
||||
|
||||
The example above declares global variables as shown below
|
||||
|
||||
- `$GLOBALS`
|
||||
- `DB` of type `MyVendor\DatabaseConnection`
|
||||
- `VIEW` of type `MyVendor\TemplateView`
|
||||
- `$_GET`
|
||||
- `data` e.g. like `["id" => "123", "title" => "Nice"]`
|
||||
|
||||
#### <forbiddenFunctions>
|
||||
Optional. Allows you to specify a list of functions that should emit the [`ForbiddenCode`](issues/ForbiddenCode.md) issue type.
|
||||
|
||||
```xml
|
||||
<forbiddenFunctions>
|
||||
<function name="var_dump" />
|
||||
<function name="dd" />
|
||||
</forbiddenFunctions>
|
||||
```
|
||||
|
||||
## Accessing Psalm configuration in plugins
|
||||
|
||||
Plugins can access or modify the global configuration in plugins using
|
||||
[singleton Psalm\Config](https://github.com/vimeo/psalm/blob/master/src/Psalm/Config.php).
|
||||
|
||||
```php
|
||||
$config = \Psalm\Config::getInstance();
|
||||
if (!isset($config->globals['$GLOBALS'])) {
|
||||
$config->globals['$GLOBALS'] = 'array{data: array<string, string>}';
|
||||
}
|
||||
```
|
||||
167
vendor/vimeo/psalm/docs/running_psalm/dealing_with_code_issues.md
vendored
Normal file
167
vendor/vimeo/psalm/docs/running_psalm/dealing_with_code_issues.md
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
# Dealing with code issues
|
||||
|
||||
Psalm has a large number of [code issues](issues.md). Each project can specify its own reporting level for a given issue.
|
||||
|
||||
Code issue levels in Psalm fall into three categories:
|
||||
|
||||
- `error`<br>
|
||||
This will cause Psalm to print a message, and to ultimately terminate with a non-zero exit status
|
||||
|
||||
- `info`<br>
|
||||
This will cause Psalm to print a message
|
||||
|
||||
- `suppress`<br>
|
||||
This will cause Psalm to ignore the code issue entirely
|
||||
|
||||
The third category, `suppress`, is the one you will probably be most interested in, especially when introducing Psalm to a large codebase.
|
||||
|
||||
## Suppressing issues
|
||||
|
||||
There are two ways to suppress an issue – via the Psalm config or via a function docblock.
|
||||
|
||||
### Config suppression
|
||||
|
||||
You can use the `<issueHandlers>` tag in the config file to influence how issues are treated.
|
||||
|
||||
Some issue types allow the use of `referencedMethod`, `referencedClass` or `referencedVariable` to isolate known trouble spots.
|
||||
|
||||
```xml
|
||||
<issueHandlers>
|
||||
<MissingPropertyType errorLevel="suppress" />
|
||||
|
||||
<InvalidReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="some_bad_directory" /> <!-- all InvalidReturnType issues in this directory are suppressed -->
|
||||
<file name="some_bad_file.php" /> <!-- all InvalidReturnType issues in this file are suppressed -->
|
||||
</errorLevel>
|
||||
</InvalidReturnType>
|
||||
<UndefinedMethod>
|
||||
<errorLevel type="suppress">
|
||||
<referencedMethod name="Bar\Bat::bar" /> <!-- not supported for all types of errors -->
|
||||
<file name="some_bad_file.php" />
|
||||
</errorLevel>
|
||||
</UndefinedMethod>
|
||||
<UndefinedClass>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="Bar\Bat\Baz" />
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
<PropertyNotSetInConstructor>
|
||||
<errorLevel type="suppress">
|
||||
<referencedProperty name="Symfony\Component\Validator\ConstraintValidator::$context" />
|
||||
</errorLevel>
|
||||
</PropertyNotSetInConstructor>
|
||||
<UndefinedGlobalVariable>
|
||||
<errorLevel type="suppress">
|
||||
<referencedVariable name="$fooBar" /> <!-- if your variable is "$fooBar" -->
|
||||
</errorLevel>
|
||||
</UndefinedGlobalVariable>
|
||||
<PluginIssue name="IssueNameEmittedByPlugin" errorLevel="info" /> <!-- this is a special case to handle issues emitted by plugins -->
|
||||
</issueHandlers>
|
||||
```
|
||||
|
||||
### Docblock suppression
|
||||
|
||||
You can also use `@psalm-suppress IssueName` on a function's docblock to suppress Psalm issues e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @psalm-suppress InvalidReturnType
|
||||
*/
|
||||
function (int $a) : string {
|
||||
return $a;
|
||||
}
|
||||
```
|
||||
|
||||
You can also suppress issues at the line level e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* @psalm-suppress InvalidReturnType
|
||||
*/
|
||||
function (int $a) : string {
|
||||
/**
|
||||
* @psalm-suppress InvalidReturnStatement
|
||||
*/
|
||||
return $a;
|
||||
}
|
||||
```
|
||||
|
||||
If you wish to suppress all issues, you can use `@psalm-suppress all` instead of multiple annotations.
|
||||
|
||||
## Using a baseline file
|
||||
|
||||
If you have a bunch of errors and you don't want to fix them all at once, Psalm can grandfather-in errors in existing code, while ensuring that new code doesn't have those same sorts of errors.
|
||||
|
||||
```
|
||||
vendor/bin/psalm --set-baseline=your-baseline.xml
|
||||
```
|
||||
|
||||
will generate a file containing the current errors. You should commit that generated file so that Psalm can use it when running in other places (e.g. CI). It won't complain about those errors either.
|
||||
|
||||
You have two options to use the generated baseline when running psalm:
|
||||
|
||||
```
|
||||
vendor/bin/psalm --use-baseline=your-baseline.xml
|
||||
```
|
||||
|
||||
or using the configuration:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
...
|
||||
errorBaseline="./path/to/your-baseline.xml"
|
||||
>
|
||||
...
|
||||
</psalm>
|
||||
```
|
||||
|
||||
To update that baseline file, use
|
||||
|
||||
```
|
||||
vendor/bin/psalm --update-baseline
|
||||
```
|
||||
|
||||
This will remove fixed issues, but will _not_ add new issues. To add new issues, use `--set-baseline=...`.
|
||||
|
||||
In case you want to run psalm without the baseline, run
|
||||
|
||||
```
|
||||
vendor/bin/psalm --ignore-baseline
|
||||
```
|
||||
|
||||
Baseline files are a great way to gradually improve a codebase.
|
||||
|
||||
## Using a plugin
|
||||
|
||||
If you want something more custom, like suppressing a certain type of error on classes that implement a particular interface, you can use a plugin that implements `AfterClassLikeVisitInterface`
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Foo\Bar;
|
||||
|
||||
use Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface;
|
||||
use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Suppress issues dynamically based on interface implementation
|
||||
*/
|
||||
class DynamicallySuppressClassIssueBasedOnInterface implements AfterClassLikeVisitInterface
|
||||
{
|
||||
public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event)
|
||||
{
|
||||
$storage = $event->getStorage();
|
||||
if ($storage->user_defined
|
||||
&& !$storage->is_interface
|
||||
&& \class_exists($storage->name)
|
||||
&& (new ReflectionClass($storage->name))->implementsInterface(\Your\Interface::class)
|
||||
) {
|
||||
$storage->suppressed_issues[-1] = 'PropertyNotSetInConstructor';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
311
vendor/vimeo/psalm/docs/running_psalm/error_levels.md
vendored
Normal file
311
vendor/vimeo/psalm/docs/running_psalm/error_levels.md
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
# Error levels
|
||||
|
||||
You can run Psalm in at different levels of strictness from 1 to 8.
|
||||
|
||||
Level 1 is the most strict, level 8 is the most lenient.
|
||||
|
||||
When no level is explicitly defined, psalm defaults to level 2.
|
||||
|
||||
Some issues are always treated as errors. These are issues with a very low probability of false-positives.
|
||||
|
||||
At level 1 all issues (except those emitted for opt-in features) that Psalm can find are treated as errors. Those issues include any situation where Psalm cannot infer the type of a given expression.
|
||||
|
||||
At level 2 Psalm ignores those `Mixed*` issues, but treats most other issues as errors.
|
||||
|
||||
At level 3 Psalm starts to be a little more lenient. For example Psalm allows missing param types, return types and property types.
|
||||
|
||||
At level 4 Psalm ignores issues for _possible_ problems. These are more likely to be false positives – where the application code may guarantee behaviour that Psalm isn't able to infer.
|
||||
|
||||
Level 5 and above allows a more non-verifiable code, and higher levels are even more permissive.
|
||||
|
||||
## Always treated as errors
|
||||
|
||||
- [AbstractMethodCall](issues/AbstractMethodCall.md)
|
||||
- [ComplexFunction](issues/ComplexFunction.md)
|
||||
- [ComplexMethod](issues/ComplexMethod.md)
|
||||
- [ConfigIssue](issues/ConfigIssue.md)
|
||||
- [DuplicateArrayKey](issues/DuplicateArrayKey.md)
|
||||
- [DuplicateClass](issues/DuplicateClass.md)
|
||||
- [DuplicateFunction](issues/DuplicateFunction.md)
|
||||
- [DuplicateMethod](issues/DuplicateMethod.md)
|
||||
- [DuplicateParam](issues/DuplicateParam.md)
|
||||
- [EmptyArrayAccess](issues/EmptyArrayAccess.md)
|
||||
- [ExtensionRequirementViolation](issues/ExtensionRequirementViolation.md)
|
||||
- [ImplementationRequirementViolation](issues/ImplementationRequirementViolation.md)
|
||||
- [ImpureByReferenceAssignment](issues/ImpureByReferenceAssignment.md)
|
||||
- [ImpureFunctionCall](issues/ImpureFunctionCall.md)
|
||||
- [ImpureMethodCall](issues/ImpureMethodCall.md)
|
||||
- [ImpurePropertyAssignment](issues/ImpurePropertyAssignment.md)
|
||||
- [ImpurePropertyFetch](issues/ImpurePropertyFetch.md)
|
||||
- [ImpureStaticProperty](issues/ImpureStaticProperty.md)
|
||||
- [ImpureStaticVariable](issues/ImpureStaticVariable.md)
|
||||
- [ImpureVariable](issues/ImpureVariable.md)
|
||||
- [InaccessibleClassConstant](issues/InaccessibleClassConstant.md)
|
||||
- [InaccessibleMethod](issues/InaccessibleMethod.md)
|
||||
- [InaccessibleProperty](issues/InaccessibleProperty.md)
|
||||
- [InterfaceInstantiation](issues/InterfaceInstantiation.md)
|
||||
- [InvalidAttribute](issues/InvalidAttribute.md)
|
||||
- [InvalidEnumMethod](issues/InvalidEnumMethod.md)
|
||||
- [InvalidExtendClass](issues/InvalidExtendClass.md)
|
||||
- [InvalidGlobal](issues/InvalidGlobal.md)
|
||||
- [InvalidInterfaceImplementation](issues/InvalidInterfaceImplementation.md)
|
||||
- [InvalidParamDefault](issues/InvalidParamDefault.md)
|
||||
- [InvalidParent](issues/InvalidParent.md)
|
||||
- [InvalidPassByReference](issues/InvalidPassByReference.md)
|
||||
- [InvalidScope](issues/InvalidScope.md)
|
||||
- [InvalidStaticInvocation](issues/InvalidStaticInvocation.md)
|
||||
- [InvalidThrow](issues/InvalidThrow.md)
|
||||
- [LoopInvalidation](issues/LoopInvalidation.md)
|
||||
- [MethodSignatureMustOmitReturnType](issues/MethodSignatureMustOmitReturnType.md)
|
||||
- [MethodSignatureMustProvideReturnType](issues/MethodSignatureMustProvideReturnType.md)
|
||||
- [MissingDependency](issues/MissingDependency.md)
|
||||
- [MissingFile](issues/MissingFile.md)
|
||||
- [MissingImmutableAnnotation](issues/MissingImmutableAnnotation.md)
|
||||
- [MissingTemplateParam](issues/MissingTemplateParam.md)
|
||||
- [MissingThrowsDocblock](issues/MissingThrowsDocblock.md)
|
||||
- [NonStaticSelfCall](issues/NonStaticSelfCall.md)
|
||||
- [NoValue](issues/NoValue.md)
|
||||
- [NullArrayAccess](issues/NullArrayAccess.md)
|
||||
- [NullFunctionCall](issues/NullFunctionCall.md)
|
||||
- [NullIterator](issues/NullIterator.md)
|
||||
- [NullPropertyAssignment](issues/NullPropertyAssignment.md)
|
||||
- [NullPropertyFetch](issues/NullPropertyFetch.md)
|
||||
- [NullReference](issues/NullReference.md)
|
||||
- [OverriddenPropertyAccess](issues/OverriddenPropertyAccess.md)
|
||||
- [ParadoxicalCondition](issues/ParadoxicalCondition.md)
|
||||
- [ParentNotFound](issues/ParentNotFound.md)
|
||||
- [TooFewArguments](issues/TooFewArguments.md)
|
||||
- [UndefinedAttributeClass](issues/UndefinedAttributeClass.md)
|
||||
- [UndefinedClass](issues/UndefinedClass.md)
|
||||
- [UndefinedConstant](issues/UndefinedConstant.md)
|
||||
- [UndefinedDocblockClass](issues/UndefinedDocblockClass.md)
|
||||
- [UndefinedFunction](issues/UndefinedFunction.md)
|
||||
- [UndefinedGlobalVariable](issues/UndefinedGlobalVariable.md)
|
||||
- [UndefinedInterface](issues/UndefinedInterface.md)
|
||||
- [UndefinedTrait](issues/UndefinedTrait.md)
|
||||
- [UndefinedVariable](issues/UndefinedVariable.md)
|
||||
- [UnimplementedAbstractMethod](issues/UnimplementedAbstractMethod.md)
|
||||
- [UnimplementedInterfaceMethod](issues/UnimplementedInterfaceMethod.md)
|
||||
- [UnrecognizedExpression](issues/UnrecognizedExpression.md)
|
||||
- [UnrecognizedStatement](issues/UnrecognizedStatement.md)
|
||||
- [UnusedFunctionCall](issues/UnusedFunctionCall.md)
|
||||
- [UnusedMethodCall](issues/UnusedMethodCall.md)
|
||||
|
||||
## Errors that appear at level 7 and below
|
||||
|
||||
- [AbstractInstantiation](issues/AbstractInstantiation.md)
|
||||
- [AssignmentToVoid](issues/AssignmentToVoid.md)
|
||||
- [CircularReference](issues/CircularReference.md)
|
||||
- [ConflictingReferenceConstraint](issues/ConflictingReferenceConstraint.md)
|
||||
- [ContinueOutsideLoop](issues/ContinueOutsideLoop.md)
|
||||
- [InvalidTypeImport](issues/InvalidTypeImport.md)
|
||||
- [MethodSignatureMismatch](issues/MethodSignatureMismatch.md)
|
||||
- [OverriddenMethodAccess](issues/OverriddenMethodAccess.md)
|
||||
- [ParamNameMismatch](issues/ParamNameMismatch.md)
|
||||
- [ReservedWord](issues/ReservedWord.md)
|
||||
- [UnhandledMatchCondition](issues/UnhandledMatchCondition.md)
|
||||
- [UninitializedProperty](issues/UninitializedProperty.md)
|
||||
|
||||
## Errors that appear at level 6 and below
|
||||
|
||||
- [InvalidArgument](issues/InvalidArgument.md)
|
||||
- [InvalidArrayAccess](issues/InvalidArrayAccess.md)
|
||||
- [InvalidArrayAssignment](issues/InvalidArrayAssignment.md)
|
||||
- [InvalidArrayOffset](issues/InvalidArrayOffset.md)
|
||||
- [InvalidCast](issues/InvalidCast.md)
|
||||
- [InvalidCatch](issues/InvalidCatch.md)
|
||||
- [InvalidClass](issues/InvalidClass.md)
|
||||
- [InvalidClone](issues/InvalidClone.md)
|
||||
- [InvalidFunctionCall](issues/InvalidFunctionCall.md)
|
||||
- [InvalidIterator](issues/InvalidIterator.md)
|
||||
- [InvalidMethodCall](issues/InvalidMethodCall.md)
|
||||
- [InvalidNamedArgument](issues/InvalidNamedArgument.md)
|
||||
- [InvalidPropertyAssignment](issues/InvalidPropertyAssignment.md)
|
||||
- [InvalidPropertyAssignmentValue](issues/InvalidPropertyAssignmentValue.md)
|
||||
- [InvalidPropertyFetch](issues/InvalidPropertyFetch.md)
|
||||
- [InvalidReturnStatement](issues/InvalidReturnStatement.md)
|
||||
- [InvalidReturnType](issues/InvalidReturnType.md)
|
||||
- [InvalidTemplateParam](issues/InvalidTemplateParam.md)
|
||||
- [NullArgument](issues/NullArgument.md)
|
||||
- [NullArrayOffset](issues/NullArrayOffset.md)
|
||||
- [TooManyTemplateParams](issues/TooManyTemplateParams.md)
|
||||
- [TraitMethodSignatureMismatch](issues/TraitMethodSignatureMismatch.md)
|
||||
- [UndefinedMethod](issues/UndefinedMethod.md)
|
||||
- [UndefinedPropertyAssignment](issues/UndefinedPropertyAssignment.md)
|
||||
- [UndefinedPropertyFetch](issues/UndefinedPropertyFetch.md)
|
||||
- [UndefinedThisPropertyFetch](issues/UndefinedThisPropertyFetch.md)
|
||||
|
||||
## Errors that appear at level 5 and below
|
||||
|
||||
- [ConstructorSignatureMismatch](issues/ConstructorSignatureMismatch.md)
|
||||
- [FalsableReturnStatement](issues/FalsableReturnStatement.md)
|
||||
- [InvalidNullableReturnType](issues/InvalidNullableReturnType.md)
|
||||
- [LessSpecificImplementedReturnType](issues/LessSpecificImplementedReturnType.md)
|
||||
- [MoreSpecificImplementedParamType](issues/MoreSpecificImplementedParamType.md)
|
||||
- [NullableReturnStatement](issues/NullableReturnStatement.md)
|
||||
- [UndefinedInterfaceMethod](issues/UndefinedInterfaceMethod.md)
|
||||
- [UndefinedThisPropertyAssignment](issues/UndefinedThisPropertyAssignment.md)
|
||||
|
||||
## Errors that appear at level 4 and below
|
||||
|
||||
- [FalseOperand](issues/FalseOperand.md)
|
||||
- [ForbiddenCode](issues/ForbiddenCode.md)
|
||||
- [ImplementedParamTypeMismatch](issues/ImplementedParamTypeMismatch.md)
|
||||
- [ImplementedReturnTypeMismatch](issues/ImplementedReturnTypeMismatch.md)
|
||||
- [ImplicitToStringCast](issues/ImplicitToStringCast.md)
|
||||
- [InternalClass](issues/InternalClass.md)
|
||||
- [InternalMethod](issues/InternalMethod.md)
|
||||
- [InternalProperty](issues/InternalProperty.md)
|
||||
- [InvalidDocblock](issues/InvalidDocblock.md)
|
||||
- [InvalidLiteralArgument](issues/InvalidLiteralArgument.md)
|
||||
- [InvalidOperand](issues/InvalidOperand.md)
|
||||
- [InvalidScalarArgument](issues/InvalidScalarArgument.md)
|
||||
- [InvalidToString](issues/InvalidToString.md)
|
||||
- [MismatchingDocblockParamType](issues/MismatchingDocblockParamType.md)
|
||||
- [MismatchingDocblockReturnType](issues/MismatchingDocblockReturnType.md)
|
||||
- [MissingDocblockType](issues/MissingDocblockType.md)
|
||||
- [NoInterfaceProperties](issues/NoInterfaceProperties.md)
|
||||
- [PossibleRawObjectIteration](issues/PossibleRawObjectIteration.md)
|
||||
- [RedundantCondition](issues/RedundantCondition.md)
|
||||
- [RedundantFunctionCall](issues/RedundantFunctionCall.md)
|
||||
- [RedundantPropertyInitializationCheck](issues/RedundantPropertyInitializationCheck.md)
|
||||
- [StringIncrement](issues/StringIncrement.md)
|
||||
- [TooManyArguments](issues/TooManyArguments.md)
|
||||
- [TypeDoesNotContainNull](issues/TypeDoesNotContainNull.md)
|
||||
- [TypeDoesNotContainType](issues/TypeDoesNotContainType.md)
|
||||
- [UndefinedMagicMethod](issues/UndefinedMagicMethod.md)
|
||||
- [UndefinedMagicPropertyAssignment](issues/UndefinedMagicPropertyAssignment.md)
|
||||
- [UndefinedMagicPropertyFetch](issues/UndefinedMagicPropertyFetch.md)
|
||||
|
||||
## Errors that appear at level 3 and below
|
||||
|
||||
- [ArgumentTypeCoercion](issues/ArgumentTypeCoercion.md)
|
||||
- [LessSpecificReturnStatement](issues/LessSpecificReturnStatement.md)
|
||||
- [MoreSpecificReturnType](issues/MoreSpecificReturnType.md)
|
||||
- [PossiblyFalseArgument](issues/PossiblyFalseArgument.md)
|
||||
- [PossiblyFalseIterator](issues/PossiblyFalseIterator.md)
|
||||
- [PossiblyFalseOperand](issues/PossiblyFalseOperand.md)
|
||||
- [PossiblyFalsePropertyAssignmentValue](issues/PossiblyFalsePropertyAssignmentValue.md)
|
||||
- [PossiblyFalseReference](issues/PossiblyFalseReference.md)
|
||||
- [PossiblyInvalidArgument](issues/PossiblyInvalidArgument.md)
|
||||
- [PossiblyInvalidArrayAccess](issues/PossiblyInvalidArrayAccess.md)
|
||||
- [PossiblyInvalidArrayAssignment](issues/PossiblyInvalidArrayAssignment.md)
|
||||
- [PossiblyInvalidArrayOffset](issues/PossiblyInvalidArrayOffset.md)
|
||||
- [PossiblyInvalidCast](issues/PossiblyInvalidCast.md)
|
||||
- [PossiblyInvalidClone](issues/PossiblyInvalidClone.md)
|
||||
- [PossiblyInvalidFunctionCall](issues/PossiblyInvalidFunctionCall.md)
|
||||
- [PossiblyInvalidIterator](issues/PossiblyInvalidIterator.md)
|
||||
- [PossiblyInvalidMethodCall](issues/PossiblyInvalidMethodCall.md)
|
||||
- [PossiblyInvalidOperand](issues/PossiblyInvalidOperand.md)
|
||||
- [PossiblyInvalidPropertyAssignment](issues/PossiblyInvalidPropertyAssignment.md)
|
||||
- [PossiblyInvalidPropertyAssignmentValue](issues/PossiblyInvalidPropertyAssignmentValue.md)
|
||||
- [PossiblyInvalidPropertyFetch](issues/PossiblyInvalidPropertyFetch.md)
|
||||
- [PossiblyNullArgument](issues/PossiblyNullArgument.md)
|
||||
- [PossiblyNullArrayAccess](issues/PossiblyNullArrayAccess.md)
|
||||
- [PossiblyNullArrayAssignment](issues/PossiblyNullArrayAssignment.md)
|
||||
- [PossiblyNullArrayOffset](issues/PossiblyNullArrayOffset.md)
|
||||
- [PossiblyNullFunctionCall](issues/PossiblyNullFunctionCall.md)
|
||||
- [PossiblyNullIterator](issues/PossiblyNullIterator.md)
|
||||
- [PossiblyNullPropertyAssignment](issues/PossiblyNullPropertyAssignment.md)
|
||||
- [PossiblyNullPropertyAssignmentValue](issues/PossiblyNullPropertyAssignmentValue.md)
|
||||
- [PossiblyNullPropertyFetch](issues/PossiblyNullPropertyFetch.md)
|
||||
- [PossiblyNullReference](issues/PossiblyNullReference.md)
|
||||
- [PossiblyUndefinedArrayOffset](issues/PossiblyUndefinedArrayOffset.md)
|
||||
- [PossiblyUndefinedGlobalVariable](issues/PossiblyUndefinedGlobalVariable.md)
|
||||
- [PossiblyUndefinedMethod](issues/PossiblyUndefinedMethod.md)
|
||||
- [PossiblyUndefinedVariable](issues/PossiblyUndefinedVariable.md)
|
||||
- [PropertyTypeCoercion](issues/PropertyTypeCoercion.md)
|
||||
- [RiskyCast](issues/RiskyCast.md)
|
||||
|
||||
## Errors that appear at level 2 and below
|
||||
|
||||
- [DeprecatedClass](issues/DeprecatedClass.md)
|
||||
- [DeprecatedConstant](issues/DeprecatedConstant.md)
|
||||
- [DeprecatedFunction](issues/DeprecatedFunction.md)
|
||||
- [DeprecatedInterface](issues/DeprecatedInterface.md)
|
||||
- [DeprecatedMethod](issues/DeprecatedMethod.md)
|
||||
- [DeprecatedProperty](issues/DeprecatedProperty.md)
|
||||
- [DeprecatedTrait](issues/DeprecatedTrait.md)
|
||||
- [DirectConstructorCall](issues/DirectConstructorCall.md)
|
||||
- [DocblockTypeContradiction](issues/DocblockTypeContradiction.md)
|
||||
- [InvalidDocblockParamName](issues/InvalidDocblockParamName.md)
|
||||
- [InvalidFalsableReturnType](issues/InvalidFalsableReturnType.md)
|
||||
- [InvalidStringClass](issues/InvalidStringClass.md)
|
||||
- [MissingClosureParamType](issues/MissingClosureParamType.md)
|
||||
- [MissingClosureReturnType](issues/MissingClosureReturnType.md)
|
||||
- [MissingConstructor](issues/MissingConstructor.md)
|
||||
- [MissingParamType](issues/MissingParamType.md)
|
||||
- [MissingPropertyType](issues/MissingPropertyType.md)
|
||||
- [MissingReturnType](issues/MissingReturnType.md)
|
||||
- [NullOperand](issues/NullOperand.md)
|
||||
- [PropertyNotSetInConstructor](issues/PropertyNotSetInConstructor.md)
|
||||
- [RawObjectIteration](issues/RawObjectIteration.md)
|
||||
- [RedundantConditionGivenDocblockType](issues/RedundantConditionGivenDocblockType.md)
|
||||
- [RedundantFunctionCallGivenDocblockType](issues/RedundantFunctionCallGivenDocblockType.md)
|
||||
- [ReferenceConstraintViolation](issues/ReferenceConstraintViolation.md)
|
||||
- [UndefinedTrace](issues/UndefinedTrace.md)
|
||||
- [UnresolvableInclude](issues/UnresolvableInclude.md)
|
||||
- [UnsafeInstantiation](issues/UnsafeInstantiation.md)
|
||||
|
||||
## Errors that only appear at level 1
|
||||
|
||||
- [LessSpecificReturnType](issues/LessSpecificReturnType.md)
|
||||
- [MixedArgument](issues/MixedArgument.md)
|
||||
- [MixedArgumentTypeCoercion](issues/MixedArgumentTypeCoercion.md)
|
||||
- [MixedArrayAccess](issues/MixedArrayAccess.md)
|
||||
- [MixedArrayAssignment](issues/MixedArrayAssignment.md)
|
||||
- [MixedArrayOffset](issues/MixedArrayOffset.md)
|
||||
- [MixedArrayTypeCoercion](issues/MixedArrayTypeCoercion.md)
|
||||
- [MixedAssignment](issues/MixedAssignment.md)
|
||||
- [MixedClone](issues/MixedClone.md)
|
||||
- [MixedFunctionCall](issues/MixedFunctionCall.md)
|
||||
- [MixedInferredReturnType](issues/MixedInferredReturnType.md)
|
||||
- [MixedMethodCall](issues/MixedMethodCall.md)
|
||||
- [MixedOperand](issues/MixedOperand.md)
|
||||
- [MixedPropertyAssignment](issues/MixedPropertyAssignment.md)
|
||||
- [MixedPropertyFetch](issues/MixedPropertyFetch.md)
|
||||
- [MixedPropertyTypeCoercion](issues/MixedPropertyTypeCoercion.md)
|
||||
- [MixedReturnStatement](issues/MixedReturnStatement.md)
|
||||
- [MixedReturnTypeCoercion](issues/MixedReturnTypeCoercion.md)
|
||||
- [MixedStringOffsetAssignment](issues/MixedStringOffsetAssignment.md)
|
||||
- [MutableDependency](issues/MutableDependency.md)
|
||||
- [PossiblyNullOperand](issues/PossiblyNullOperand.md)
|
||||
- [RedundantIdentityWithTrue](issues/RedundantIdentityWithTrue.md)
|
||||
- [Trace](issues/Trace.md)
|
||||
|
||||
## Feature-specific errors
|
||||
|
||||
- [PossiblyUndefinedIntArrayOffset](issues/PossiblyUndefinedIntArrayOffset.md)
|
||||
- [PossiblyUndefinedStringArrayOffset](issues/PossiblyUndefinedStringArrayOffset.md)
|
||||
- [PossiblyUnusedMethod](issues/PossiblyUnusedMethod.md)
|
||||
- [PossiblyUnusedParam](issues/PossiblyUnusedParam.md)
|
||||
- [PossiblyUnusedProperty](issues/PossiblyUnusedProperty.md)
|
||||
- [TaintedCallable](issues/TaintedCallable.md)
|
||||
- [TaintedCookie](issues/TaintedCookie.md)
|
||||
- [TaintedCustom](issues/TaintedCustom.md)
|
||||
- [TaintedEval](issues/TaintedEval.md)
|
||||
- [TaintedFile](issues/TaintedFile.md)
|
||||
- [TaintedHeader](issues/TaintedHeader.md)
|
||||
- [TaintedHtml](issues/TaintedHtml.md)
|
||||
- [TaintedInclude](issues/TaintedInclude.md)
|
||||
- [TaintedInput](issues/TaintedInput.md)
|
||||
- [TaintedLdap](issues/TaintedLdap.md)
|
||||
- [TaintedShell](issues/TaintedShell.md)
|
||||
- [TaintedSql](issues/TaintedSql.md)
|
||||
- [TaintedSSRF](issues/TaintedSSRF.md)
|
||||
- [TaintedSystemSecret](issues/TaintedSystemSecret.md)
|
||||
- [TaintedUnserialize](issues/TaintedUnserialize.md)
|
||||
- [TaintedUserSecret](issues/TaintedUserSecret.md)
|
||||
- [UncaughtThrowInGlobalScope](issues/UncaughtThrowInGlobalScope.md)
|
||||
- [UnevaluatedCode](issues/UnevaluatedCode.md)
|
||||
- [UnnecessaryVarAnnotation](issues/UnnecessaryVarAnnotation.md)
|
||||
- [UnusedClass](issues/UnusedClass.md)
|
||||
- [UnusedClosureParam](issues/UnusedClosureParam.md)
|
||||
- [UnusedConstructor](issues/UnusedConstructor.md)
|
||||
- [UnusedDocblockParam](issues/UnusedDocblockParam.md)
|
||||
- [UnusedForeachValue](issues/UnusedForeachValue.md)
|
||||
- [UnusedMethod](issues/UnusedMethod.md)
|
||||
- [UnusedParam](issues/UnusedParam.md)
|
||||
- [UnusedProperty](issues/UnusedProperty.md)
|
||||
- [UnusedPsalmSuppress](issues/UnusedPsalmSuppress.md)
|
||||
- [UnusedVariable](issues/UnusedVariable.md)
|
||||
53
vendor/vimeo/psalm/docs/running_psalm/installation.md
vendored
Normal file
53
vendor/vimeo/psalm/docs/running_psalm/installation.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Installation
|
||||
|
||||
The latest version of Psalm requires PHP >= 7.4 and [Composer](https://getcomposer.org/).
|
||||
|
||||
```bash
|
||||
composer require --dev vimeo/psalm
|
||||
```
|
||||
|
||||
Generate a config file:
|
||||
|
||||
```bash
|
||||
./vendor/bin/psalm --init
|
||||
```
|
||||
|
||||
Psalm will scan your project and figure out an appropriate [error level](error_levels.md) for your codebase.
|
||||
|
||||
Then run Psalm:
|
||||
|
||||
```bash
|
||||
./vendor/bin/psalm
|
||||
```
|
||||
|
||||
Psalm will probably find a number of issues - find out how to deal with them in [Dealing with code issues](dealing_with_code_issues.md).
|
||||
|
||||
## Installing plugins
|
||||
|
||||
While Psalm can figure out the types used by various libraries based on
|
||||
their source code and docblocks, it works even better with custom-tailored types
|
||||
provided by Psalm plugins.
|
||||
|
||||
Check out the [list of existing plugins on Packagist](https://packagist.org/?type=psalm-plugin).
|
||||
Install them with `composer require --dev <plugin/package> && vendor/bin/psalm-plugin enable <plugin/package>`
|
||||
|
||||
Read more about plugins in [Using Plugins chapter](plugins/using_plugins.md).
|
||||
|
||||
## Using the Phar
|
||||
|
||||
Sometimes your project can conflict with one or more of Psalm’s dependencies. In
|
||||
that case you may find the Phar (a self-contained PHP executable) useful.
|
||||
|
||||
The Phar can be downloaded from Github:
|
||||
|
||||
```bash
|
||||
wget https://github.com/vimeo/psalm/releases/latest/download/psalm.phar
|
||||
chmod +x psalm.phar
|
||||
./psalm.phar --version
|
||||
```
|
||||
|
||||
Alternatively, you can use Composer to install the Phar:
|
||||
|
||||
```bash
|
||||
composer require --dev psalm/phar
|
||||
```
|
||||
300
vendor/vimeo/psalm/docs/running_psalm/issues.md
vendored
Normal file
300
vendor/vimeo/psalm/docs/running_psalm/issues.md
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
# Issue types
|
||||
|
||||
- [AbstractInstantiation](issues/AbstractInstantiation.md)
|
||||
- [AbstractMethodCall](issues/AbstractMethodCall.md)
|
||||
- [AmbiguousConstantInheritance](issues/AmbiguousConstantInheritance.md)
|
||||
- [ArgumentTypeCoercion](issues/ArgumentTypeCoercion.md)
|
||||
- [AssignmentToVoid](issues/AssignmentToVoid.md)
|
||||
- [CheckType](issues/CheckType.md)
|
||||
- [CircularReference](issues/CircularReference.md)
|
||||
- [ComplexFunction](issues/ComplexFunction.md)
|
||||
- [ComplexMethod](issues/ComplexMethod.md)
|
||||
- [ConfigIssue](issues/ConfigIssue.md)
|
||||
- [ConflictingReferenceConstraint](issues/ConflictingReferenceConstraint.md)
|
||||
- [ConstantDeclarationInTrait](issues/ConstantDeclarationInTrait.md)
|
||||
- [ConstructorSignatureMismatch](issues/ConstructorSignatureMismatch.md)
|
||||
- [ContinueOutsideLoop](issues/ContinueOutsideLoop.md)
|
||||
- [DeprecatedClass](issues/DeprecatedClass.md)
|
||||
- [DeprecatedConstant](issues/DeprecatedConstant.md)
|
||||
- [DeprecatedFunction](issues/DeprecatedFunction.md)
|
||||
- [DeprecatedInterface](issues/DeprecatedInterface.md)
|
||||
- [DeprecatedMethod](issues/DeprecatedMethod.md)
|
||||
- [DeprecatedProperty](issues/DeprecatedProperty.md)
|
||||
- [DeprecatedTrait](issues/DeprecatedTrait.md)
|
||||
- [DirectConstructorCall](issues/DirectConstructorCall.md)
|
||||
- [DocblockTypeContradiction](issues/DocblockTypeContradiction.md)
|
||||
- [DuplicateArrayKey](issues/DuplicateArrayKey.md)
|
||||
- [DuplicateClass](issues/DuplicateClass.md)
|
||||
- [DuplicateConstant](issues/DuplicateConstant.md)
|
||||
- [DuplicateEnumCase](issues/DuplicateEnumCase.md)
|
||||
- [DuplicateEnumCaseValue](issues/DuplicateEnumCaseValue.md)
|
||||
- [DuplicateFunction](issues/DuplicateFunction.md)
|
||||
- [DuplicateMethod](issues/DuplicateMethod.md)
|
||||
- [DuplicateParam](issues/DuplicateParam.md)
|
||||
- [EmptyArrayAccess](issues/EmptyArrayAccess.md)
|
||||
- [ExtensionRequirementViolation](issues/ExtensionRequirementViolation.md)
|
||||
- [FalsableReturnStatement](issues/FalsableReturnStatement.md)
|
||||
- [FalseOperand](issues/FalseOperand.md)
|
||||
- [ForbiddenCode](issues/ForbiddenCode.md)
|
||||
- [IfThisIsMismatch](issues/IfThisIsMismatch.md)
|
||||
- [ImplementationRequirementViolation](issues/ImplementationRequirementViolation.md)
|
||||
- [ImplementedParamTypeMismatch](issues/ImplementedParamTypeMismatch.md)
|
||||
- [ImplementedReturnTypeMismatch](issues/ImplementedReturnTypeMismatch.md)
|
||||
- [ImplicitToStringCast](issues/ImplicitToStringCast.md)
|
||||
- [ImpureByReferenceAssignment](issues/ImpureByReferenceAssignment.md)
|
||||
- [ImpureFunctionCall](issues/ImpureFunctionCall.md)
|
||||
- [ImpureMethodCall](issues/ImpureMethodCall.md)
|
||||
- [ImpurePropertyAssignment](issues/ImpurePropertyAssignment.md)
|
||||
- [ImpurePropertyFetch](issues/ImpurePropertyFetch.md)
|
||||
- [ImpureStaticProperty](issues/ImpureStaticProperty.md)
|
||||
- [ImpureStaticVariable](issues/ImpureStaticVariable.md)
|
||||
- [ImpureVariable](issues/ImpureVariable.md)
|
||||
- [InaccessibleClassConstant](issues/InaccessibleClassConstant.md)
|
||||
- [InaccessibleMethod](issues/InaccessibleMethod.md)
|
||||
- [InaccessibleProperty](issues/InaccessibleProperty.md)
|
||||
- [InterfaceInstantiation](issues/InterfaceInstantiation.md)
|
||||
- [InternalClass](issues/InternalClass.md)
|
||||
- [InternalMethod](issues/InternalMethod.md)
|
||||
- [InternalProperty](issues/InternalProperty.md)
|
||||
- [InvalidArgument](issues/InvalidArgument.md)
|
||||
- [InvalidArrayAccess](issues/InvalidArrayAccess.md)
|
||||
- [InvalidArrayAssignment](issues/InvalidArrayAssignment.md)
|
||||
- [InvalidArrayOffset](issues/InvalidArrayOffset.md)
|
||||
- [InvalidAttribute](issues/InvalidAttribute.md)
|
||||
- [InvalidCast](issues/InvalidCast.md)
|
||||
- [InvalidCatch](issues/InvalidCatch.md)
|
||||
- [InvalidClass](issues/InvalidClass.md)
|
||||
- [InvalidClassConstantType](issues/InvalidClassConstantType.md)
|
||||
- [InvalidClone](issues/InvalidClone.md)
|
||||
- [InvalidConstantAssignmentValue](issues/InvalidConstantAssignmentValue.md)
|
||||
- [InvalidDocblock](issues/InvalidDocblock.md)
|
||||
- [InvalidDocblockParamName](issues/InvalidDocblockParamName.md)
|
||||
- [InvalidEnumBackingType](issues/InvalidEnumBackingType.md)
|
||||
- [InvalidEnumCaseValue](issues/InvalidEnumCaseValue.md)
|
||||
- [InvalidEnumMethod](issues/InvalidEnumMethod.md)
|
||||
- [InvalidExtendClass](issues/InvalidExtendClass.md)
|
||||
- [InvalidFalsableReturnType](issues/InvalidFalsableReturnType.md)
|
||||
- [InvalidFunctionCall](issues/InvalidFunctionCall.md)
|
||||
- [InvalidGlobal](issues/InvalidGlobal.md)
|
||||
- [InvalidInterfaceImplementation](issues/InvalidInterfaceImplementation.md)
|
||||
- [InvalidIterator](issues/InvalidIterator.md)
|
||||
- [InvalidLiteralArgument](issues/InvalidLiteralArgument.md)
|
||||
- [InvalidMethodCall](issues/InvalidMethodCall.md)
|
||||
- [InvalidNamedArgument](issues/InvalidNamedArgument.md)
|
||||
- [InvalidNullableReturnType](issues/InvalidNullableReturnType.md)
|
||||
- [InvalidOperand](issues/InvalidOperand.md)
|
||||
- [InvalidParamDefault](issues/InvalidParamDefault.md)
|
||||
- [InvalidParent](issues/InvalidParent.md)
|
||||
- [InvalidPassByReference](issues/InvalidPassByReference.md)
|
||||
- [InvalidPropertyAssignment](issues/InvalidPropertyAssignment.md)
|
||||
- [InvalidPropertyAssignmentValue](issues/InvalidPropertyAssignmentValue.md)
|
||||
- [InvalidPropertyFetch](issues/InvalidPropertyFetch.md)
|
||||
- [InvalidReturnStatement](issues/InvalidReturnStatement.md)
|
||||
- [InvalidReturnType](issues/InvalidReturnType.md)
|
||||
- [InvalidScalarArgument](issues/InvalidScalarArgument.md)
|
||||
- [InvalidScope](issues/InvalidScope.md)
|
||||
- [InvalidStaticInvocation](issues/InvalidStaticInvocation.md)
|
||||
- [InvalidStringClass](issues/InvalidStringClass.md)
|
||||
- [InvalidTemplateParam](issues/InvalidTemplateParam.md)
|
||||
- [InvalidThrow](issues/InvalidThrow.md)
|
||||
- [InvalidToString](issues/InvalidToString.md)
|
||||
- [InvalidTraversableImplementation](issues/InvalidTraversableImplementation.md)
|
||||
- [InvalidTypeImport](issues/InvalidTypeImport.md)
|
||||
- [LessSpecificClassConstantType](issues/LessSpecificClassConstantType.md)
|
||||
- [LessSpecificImplementedReturnType](issues/LessSpecificImplementedReturnType.md)
|
||||
- [LessSpecificReturnStatement](issues/LessSpecificReturnStatement.md)
|
||||
- [LessSpecificReturnType](issues/LessSpecificReturnType.md)
|
||||
- [LoopInvalidation](issues/LoopInvalidation.md)
|
||||
- [MethodSignatureMismatch](issues/MethodSignatureMismatch.md)
|
||||
- [MethodSignatureMustOmitReturnType](issues/MethodSignatureMustOmitReturnType.md)
|
||||
- [MethodSignatureMustProvideReturnType](issues/MethodSignatureMustProvideReturnType.md)
|
||||
- [MismatchingDocblockParamType](issues/MismatchingDocblockParamType.md)
|
||||
- [MismatchingDocblockPropertyType](issues/MismatchingDocblockPropertyType.md)
|
||||
- [MismatchingDocblockReturnType](issues/MismatchingDocblockReturnType.md)
|
||||
- [MissingClosureParamType](issues/MissingClosureParamType.md)
|
||||
- [MissingClosureReturnType](issues/MissingClosureReturnType.md)
|
||||
- [MissingConstructor](issues/MissingConstructor.md)
|
||||
- [MissingDependency](issues/MissingDependency.md)
|
||||
- [MissingDocblockType](issues/MissingDocblockType.md)
|
||||
- [MissingFile](issues/MissingFile.md)
|
||||
- [MissingImmutableAnnotation](issues/MissingImmutableAnnotation.md)
|
||||
- [MissingParamType](issues/MissingParamType.md)
|
||||
- [MissingPropertyType](issues/MissingPropertyType.md)
|
||||
- [MissingReturnType](issues/MissingReturnType.md)
|
||||
- [MissingTemplateParam](issues/MissingTemplateParam.md)
|
||||
- [MissingThrowsDocblock](issues/MissingThrowsDocblock.md)
|
||||
- [MixedArgument](issues/MixedArgument.md)
|
||||
- [MixedArgumentTypeCoercion](issues/MixedArgumentTypeCoercion.md)
|
||||
- [MixedArrayAccess](issues/MixedArrayAccess.md)
|
||||
- [MixedArrayAssignment](issues/MixedArrayAssignment.md)
|
||||
- [MixedArrayOffset](issues/MixedArrayOffset.md)
|
||||
- [MixedArrayTypeCoercion](issues/MixedArrayTypeCoercion.md)
|
||||
- [MixedAssignment](issues/MixedAssignment.md)
|
||||
- [MixedClone](issues/MixedClone.md)
|
||||
- [MixedFunctionCall](issues/MixedFunctionCall.md)
|
||||
- [MixedInferredReturnType](issues/MixedInferredReturnType.md)
|
||||
- [MixedMethodCall](issues/MixedMethodCall.md)
|
||||
- [MixedOperand](issues/MixedOperand.md)
|
||||
- [MixedPropertyAssignment](issues/MixedPropertyAssignment.md)
|
||||
- [MixedPropertyFetch](issues/MixedPropertyFetch.md)
|
||||
- [MixedPropertyTypeCoercion](issues/MixedPropertyTypeCoercion.md)
|
||||
- [MixedReturnStatement](issues/MixedReturnStatement.md)
|
||||
- [MixedReturnTypeCoercion](issues/MixedReturnTypeCoercion.md)
|
||||
- [MixedStringOffsetAssignment](issues/MixedStringOffsetAssignment.md)
|
||||
- [MoreSpecificImplementedParamType](issues/MoreSpecificImplementedParamType.md)
|
||||
- [MoreSpecificReturnType](issues/MoreSpecificReturnType.md)
|
||||
- [MutableDependency](issues/MutableDependency.md)
|
||||
- [NamedArgumentNotAllowed](issues/NamedArgumentNotAllowed.md)
|
||||
- [NoEnumProperties](issues/NoEnumProperties.md)
|
||||
- [NoInterfaceProperties](issues/NoInterfaceProperties.md)
|
||||
- [NonInvariantDocblockPropertyType](issues/NonInvariantDocblockPropertyType.md)
|
||||
- [NonInvariantPropertyType](issues/NonInvariantPropertyType.md)
|
||||
- [NonStaticSelfCall](issues/NonStaticSelfCall.md)
|
||||
- [NoValue](issues/NoValue.md)
|
||||
- [NullableReturnStatement](issues/NullableReturnStatement.md)
|
||||
- [NullArgument](issues/NullArgument.md)
|
||||
- [NullArrayAccess](issues/NullArrayAccess.md)
|
||||
- [NullArrayOffset](issues/NullArrayOffset.md)
|
||||
- [NullFunctionCall](issues/NullFunctionCall.md)
|
||||
- [NullIterator](issues/NullIterator.md)
|
||||
- [NullOperand](issues/NullOperand.md)
|
||||
- [NullPropertyAssignment](issues/NullPropertyAssignment.md)
|
||||
- [NullPropertyFetch](issues/NullPropertyFetch.md)
|
||||
- [NullReference](issues/NullReference.md)
|
||||
- [OverriddenFinalConstant](issues/OverriddenFinalConstant.md)
|
||||
- [OverriddenInterfaceConstant](issues/OverriddenInterfaceConstant.md)
|
||||
- [OverriddenMethodAccess](issues/OverriddenMethodAccess.md)
|
||||
- [OverriddenPropertyAccess](issues/OverriddenPropertyAccess.md)
|
||||
- [ParadoxicalCondition](issues/ParadoxicalCondition.md)
|
||||
- [ParamNameMismatch](issues/ParamNameMismatch.md)
|
||||
- [ParentNotFound](issues/ParentNotFound.md)
|
||||
- [ParseError](issues/ParseError.md)
|
||||
- [PluginIssue](issues/PluginIssue.md)
|
||||
- [PossibleRawObjectIteration](issues/PossibleRawObjectIteration.md)
|
||||
- [PossiblyFalseArgument](issues/PossiblyFalseArgument.md)
|
||||
- [PossiblyFalseIterator](issues/PossiblyFalseIterator.md)
|
||||
- [PossiblyFalseOperand](issues/PossiblyFalseOperand.md)
|
||||
- [PossiblyFalsePropertyAssignmentValue](issues/PossiblyFalsePropertyAssignmentValue.md)
|
||||
- [PossiblyFalseReference](issues/PossiblyFalseReference.md)
|
||||
- [PossiblyInvalidArgument](issues/PossiblyInvalidArgument.md)
|
||||
- [PossiblyInvalidArrayAccess](issues/PossiblyInvalidArrayAccess.md)
|
||||
- [PossiblyInvalidArrayAssignment](issues/PossiblyInvalidArrayAssignment.md)
|
||||
- [PossiblyInvalidArrayOffset](issues/PossiblyInvalidArrayOffset.md)
|
||||
- [PossiblyInvalidCast](issues/PossiblyInvalidCast.md)
|
||||
- [PossiblyInvalidClone](issues/PossiblyInvalidClone.md)
|
||||
- [PossiblyInvalidDocblockTag](issues/PossiblyInvalidDocblockTag.md)
|
||||
- [PossiblyInvalidFunctionCall](issues/PossiblyInvalidFunctionCall.md)
|
||||
- [PossiblyInvalidIterator](issues/PossiblyInvalidIterator.md)
|
||||
- [PossiblyInvalidMethodCall](issues/PossiblyInvalidMethodCall.md)
|
||||
- [PossiblyInvalidOperand](issues/PossiblyInvalidOperand.md)
|
||||
- [PossiblyInvalidPropertyAssignment](issues/PossiblyInvalidPropertyAssignment.md)
|
||||
- [PossiblyInvalidPropertyAssignmentValue](issues/PossiblyInvalidPropertyAssignmentValue.md)
|
||||
- [PossiblyInvalidPropertyFetch](issues/PossiblyInvalidPropertyFetch.md)
|
||||
- [PossiblyNullArgument](issues/PossiblyNullArgument.md)
|
||||
- [PossiblyNullArrayAccess](issues/PossiblyNullArrayAccess.md)
|
||||
- [PossiblyNullArrayAssignment](issues/PossiblyNullArrayAssignment.md)
|
||||
- [PossiblyNullArrayOffset](issues/PossiblyNullArrayOffset.md)
|
||||
- [PossiblyNullFunctionCall](issues/PossiblyNullFunctionCall.md)
|
||||
- [PossiblyNullIterator](issues/PossiblyNullIterator.md)
|
||||
- [PossiblyNullOperand](issues/PossiblyNullOperand.md)
|
||||
- [PossiblyNullPropertyAssignment](issues/PossiblyNullPropertyAssignment.md)
|
||||
- [PossiblyNullPropertyAssignmentValue](issues/PossiblyNullPropertyAssignmentValue.md)
|
||||
- [PossiblyNullPropertyFetch](issues/PossiblyNullPropertyFetch.md)
|
||||
- [PossiblyNullReference](issues/PossiblyNullReference.md)
|
||||
- [PossiblyUndefinedArrayOffset](issues/PossiblyUndefinedArrayOffset.md)
|
||||
- [PossiblyUndefinedGlobalVariable](issues/PossiblyUndefinedGlobalVariable.md)
|
||||
- [PossiblyUndefinedIntArrayOffset](issues/PossiblyUndefinedIntArrayOffset.md)
|
||||
- [PossiblyUndefinedMethod](issues/PossiblyUndefinedMethod.md)
|
||||
- [PossiblyUndefinedStringArrayOffset](issues/PossiblyUndefinedStringArrayOffset.md)
|
||||
- [PossiblyUndefinedVariable](issues/PossiblyUndefinedVariable.md)
|
||||
- [PossiblyUnusedMethod](issues/PossiblyUnusedMethod.md)
|
||||
- [PossiblyUnusedParam](issues/PossiblyUnusedParam.md)
|
||||
- [PossiblyUnusedProperty](issues/PossiblyUnusedProperty.md)
|
||||
- [PossiblyUnusedReturnValue](issues/PossiblyUnusedReturnValue.md)
|
||||
- [PropertyNotSetInConstructor](issues/PropertyNotSetInConstructor.md)
|
||||
- [PropertyTypeCoercion](issues/PropertyTypeCoercion.md)
|
||||
- [RawObjectIteration](issues/RawObjectIteration.md)
|
||||
- [RedundantCast](issues/RedundantCast.md)
|
||||
- [RedundantCastGivenDocblockType](issues/RedundantCastGivenDocblockType.md)
|
||||
- [RedundantCondition](issues/RedundantCondition.md)
|
||||
- [RedundantConditionGivenDocblockType](issues/RedundantConditionGivenDocblockType.md)
|
||||
- [RedundantFunctionCall](issues/RedundantFunctionCall.md)
|
||||
- [RedundantFunctionCallGivenDocblockType](issues/RedundantFunctionCallGivenDocblockType.md)
|
||||
- [RedundantIdentityWithTrue](issues/RedundantIdentityWithTrue.md)
|
||||
- [RedundantPropertyInitializationCheck](issues/RedundantPropertyInitializationCheck.md)
|
||||
- [ReferenceConstraintViolation](issues/ReferenceConstraintViolation.md)
|
||||
- [ReferenceReusedFromConfusingScope](issues/ReferenceReusedFromConfusingScope.md)
|
||||
- [ReservedWord](issues/ReservedWord.md)
|
||||
- [RiskyCast](issues/RiskyCast.md)
|
||||
- [StringIncrement](issues/StringIncrement.md)
|
||||
- [TaintedCallable](issues/TaintedCallable.md)
|
||||
- [TaintedCookie](issues/TaintedCookie.md)
|
||||
- [TaintedCustom](issues/TaintedCustom.md)
|
||||
- [TaintedEval](issues/TaintedEval.md)
|
||||
- [TaintedFile](issues/TaintedFile.md)
|
||||
- [TaintedHeader](issues/TaintedHeader.md)
|
||||
- [TaintedHtml](issues/TaintedHtml.md)
|
||||
- [TaintedInclude](issues/TaintedInclude.md)
|
||||
- [TaintedInput](issues/TaintedInput.md)
|
||||
- [TaintedLdap](issues/TaintedLdap.md)
|
||||
- [TaintedShell](issues/TaintedShell.md)
|
||||
- [TaintedSql](issues/TaintedSql.md)
|
||||
- [TaintedSSRF](issues/TaintedSSRF.md)
|
||||
- [TaintedSystemSecret](issues/TaintedSystemSecret.md)
|
||||
- [TaintedTextWithQuotes](issues/TaintedTextWithQuotes.md)
|
||||
- [TaintedUnserialize](issues/TaintedUnserialize.md)
|
||||
- [TaintedUserSecret](issues/TaintedUserSecret.md)
|
||||
- [TooFewArguments](issues/TooFewArguments.md)
|
||||
- [TooManyArguments](issues/TooManyArguments.md)
|
||||
- [TooManyTemplateParams](issues/TooManyTemplateParams.md)
|
||||
- [Trace](issues/Trace.md)
|
||||
- [TraitMethodSignatureMismatch](issues/TraitMethodSignatureMismatch.md)
|
||||
- [TypeDoesNotContainNull](issues/TypeDoesNotContainNull.md)
|
||||
- [TypeDoesNotContainType](issues/TypeDoesNotContainType.md)
|
||||
- [UncaughtThrowInGlobalScope](issues/UncaughtThrowInGlobalScope.md)
|
||||
- [UndefinedAttributeClass](issues/UndefinedAttributeClass.md)
|
||||
- [UndefinedClass](issues/UndefinedClass.md)
|
||||
- [UndefinedConstant](issues/UndefinedConstant.md)
|
||||
- [UndefinedDocblockClass](issues/UndefinedDocblockClass.md)
|
||||
- [UndefinedFunction](issues/UndefinedFunction.md)
|
||||
- [UndefinedGlobalVariable](issues/UndefinedGlobalVariable.md)
|
||||
- [UndefinedInterface](issues/UndefinedInterface.md)
|
||||
- [UndefinedInterfaceMethod](issues/UndefinedInterfaceMethod.md)
|
||||
- [UndefinedMagicMethod](issues/UndefinedMagicMethod.md)
|
||||
- [UndefinedMagicPropertyAssignment](issues/UndefinedMagicPropertyAssignment.md)
|
||||
- [UndefinedMagicPropertyFetch](issues/UndefinedMagicPropertyFetch.md)
|
||||
- [UndefinedMethod](issues/UndefinedMethod.md)
|
||||
- [UndefinedPropertyAssignment](issues/UndefinedPropertyAssignment.md)
|
||||
- [UndefinedPropertyFetch](issues/UndefinedPropertyFetch.md)
|
||||
- [UndefinedThisPropertyAssignment](issues/UndefinedThisPropertyAssignment.md)
|
||||
- [UndefinedThisPropertyFetch](issues/UndefinedThisPropertyFetch.md)
|
||||
- [UndefinedTrace](issues/UndefinedTrace.md)
|
||||
- [UndefinedTrait](issues/UndefinedTrait.md)
|
||||
- [UndefinedVariable](issues/UndefinedVariable.md)
|
||||
- [UnevaluatedCode](issues/UnevaluatedCode.md)
|
||||
- [UnhandledMatchCondition](issues/UnhandledMatchCondition.md)
|
||||
- [UnimplementedAbstractMethod](issues/UnimplementedAbstractMethod.md)
|
||||
- [UnimplementedInterfaceMethod](issues/UnimplementedInterfaceMethod.md)
|
||||
- [UninitializedProperty](issues/UninitializedProperty.md)
|
||||
- [UnnecessaryVarAnnotation](issues/UnnecessaryVarAnnotation.md)
|
||||
- [UnrecognizedExpression](issues/UnrecognizedExpression.md)
|
||||
- [UnrecognizedStatement](issues/UnrecognizedStatement.md)
|
||||
- [UnresolvableConstant](issues/UnresolvableConstant.md)
|
||||
- [UnresolvableInclude](issues/UnresolvableInclude.md)
|
||||
- [UnsafeGenericInstantiation](issues/UnsafeGenericInstantiation.md)
|
||||
- [UnsafeInstantiation](issues/UnsafeInstantiation.md)
|
||||
- [UnsupportedReferenceUsage](issues/UnsupportedReferenceUsage.md)
|
||||
- [UnusedBaselineEntry](issues/UnusedBaselineEntry.md)
|
||||
- [UnusedClass](issues/UnusedClass.md)
|
||||
- [UnusedClosureParam](issues/UnusedClosureParam.md)
|
||||
- [UnusedConstructor](issues/UnusedConstructor.md)
|
||||
- [UnusedDocblockParam](issues/UnusedDocblockParam.md)
|
||||
- [UnusedForeachValue](issues/UnusedForeachValue.md)
|
||||
- [UnusedFunctionCall](issues/UnusedFunctionCall.md)
|
||||
- [UnusedMethod](issues/UnusedMethod.md)
|
||||
- [UnusedMethodCall](issues/UnusedMethodCall.md)
|
||||
- [UnusedParam](issues/UnusedParam.md)
|
||||
- [UnusedProperty](issues/UnusedProperty.md)
|
||||
- [UnusedPsalmSuppress](issues/UnusedPsalmSuppress.md)
|
||||
- [UnusedReturnValue](issues/UnusedReturnValue.md)
|
||||
- [UnusedVariable](issues/UnusedVariable.md)
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/AbstractInstantiation.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/AbstractInstantiation.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# AbstractInstantiation
|
||||
|
||||
Emitted when an attempt is made to instantiate an abstract class:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
abstract class A {}
|
||||
new A();
|
||||
```
|
||||
17
vendor/vimeo/psalm/docs/running_psalm/issues/AbstractMethodCall.md
vendored
Normal file
17
vendor/vimeo/psalm/docs/running_psalm/issues/AbstractMethodCall.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# AbstractMethodCall
|
||||
|
||||
Emitted when an attempt is made to call an abstract static method directly
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
abstract class Base {
|
||||
abstract static function bar() : void;
|
||||
}
|
||||
|
||||
Base::bar();
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
It's not allowed by PHP.
|
||||
41
vendor/vimeo/psalm/docs/running_psalm/issues/AmbiguousConstantInheritance.md
vendored
Normal file
41
vendor/vimeo/psalm/docs/running_psalm/issues/AmbiguousConstantInheritance.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# AmbiguousConstantInheritance
|
||||
|
||||
Emitted when a constant is inherited from multiple sources.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
interface Foo
|
||||
{
|
||||
/** @var non-empty-string */
|
||||
public const CONSTANT='foo';
|
||||
}
|
||||
|
||||
interface Bar
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
public const CONSTANT='bar';
|
||||
}
|
||||
|
||||
interface Baz extends Foo, Bar {}
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
interface Foo
|
||||
{
|
||||
/** @var non-empty-string */
|
||||
public const CONSTANT='foo';
|
||||
}
|
||||
|
||||
class Bar
|
||||
{
|
||||
/** @var non-empty-string */
|
||||
public const CONSTANT='bar';
|
||||
}
|
||||
|
||||
class Baz extends Bar implements Foo {}
|
||||
```
|
||||
39
vendor/vimeo/psalm/docs/running_psalm/issues/ArgumentTypeCoercion.md
vendored
Normal file
39
vendor/vimeo/psalm/docs/running_psalm/issues/ArgumentTypeCoercion.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# ArgumentTypeCoercion
|
||||
|
||||
Emitted when calling a function with an argument which has a less specific type than the function expects
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
|
||||
function takesA(A $a) : void {
|
||||
takesB($a);
|
||||
}
|
||||
function takesB(B $b) : void {}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
You could add a typecheck before the call to `takesB`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function takesA(A $a) : void {
|
||||
if ($a instanceof B) {
|
||||
takesB($a);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or, if you have control over the function signature of `takesA` you can change it to expect `B`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function takesA(B $a) : void {
|
||||
takesB($a);
|
||||
}
|
||||
```
|
||||
25
vendor/vimeo/psalm/docs/running_psalm/issues/AssignmentToVoid.md
vendored
Normal file
25
vendor/vimeo/psalm/docs/running_psalm/issues/AssignmentToVoid.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# AssignmentToVoid
|
||||
|
||||
Emitted when assigning from a function that returns `void`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function foo() : void {}
|
||||
$a = foo();
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
Though `void`-returning functions are treated by PHP as returning `null` (so this on its own does not lead to runtime errors), `void` is a concept more broadly in programming languages which is not designed for assignment purposes.
|
||||
|
||||
## How to fix
|
||||
|
||||
You should just be able to remove the assignment entirely:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function foo() : void {}
|
||||
foo();
|
||||
```
|
||||
19
vendor/vimeo/psalm/docs/running_psalm/issues/CheckType.md
vendored
Normal file
19
vendor/vimeo/psalm/docs/running_psalm/issues/CheckType.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# CheckType
|
||||
|
||||
Checks if a variable matches a specific type.
|
||||
Similar to [Trace](./Trace.md), but only shows if the type does not match the expected type.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @psalm-check-type $x = 1 */
|
||||
$x = 2; // Checked variable $x = 1 does not match $x = 2
|
||||
```
|
||||
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @psalm-check-type-exact $x = int */
|
||||
$x = 2; // Checked variable $x = int does not match $x = 2
|
||||
```
|
||||
14
vendor/vimeo/psalm/docs/running_psalm/issues/CircularReference.md
vendored
Normal file
14
vendor/vimeo/psalm/docs/running_psalm/issues/CircularReference.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# CircularReference
|
||||
|
||||
Emitted when a class references itself as one of its parents
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A extends B {}
|
||||
class B extends A {}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The code above will not compile
|
||||
3
vendor/vimeo/psalm/docs/running_psalm/issues/ComplexFunction.md
vendored
Normal file
3
vendor/vimeo/psalm/docs/running_psalm/issues/ComplexFunction.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# ComplexFunction
|
||||
|
||||
Emitted when a function is too complicated. Complicated functions should be split up.
|
||||
3
vendor/vimeo/psalm/docs/running_psalm/issues/ComplexMethod.md
vendored
Normal file
3
vendor/vimeo/psalm/docs/running_psalm/issues/ComplexMethod.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# ComplexMethod
|
||||
|
||||
Emitted when a method is too complicated. Complicated methods should be split up.
|
||||
7
vendor/vimeo/psalm/docs/running_psalm/issues/ConfigIssue.md
vendored
Normal file
7
vendor/vimeo/psalm/docs/running_psalm/issues/ConfigIssue.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# ConfigIssue
|
||||
|
||||
Emitted for non-fatal configuration issues, e.g. deprecated configuration switches.
|
||||
|
||||
## Why this is bad
|
||||
|
||||
Your config file may be incompatible with future Psalm versions.
|
||||
39
vendor/vimeo/psalm/docs/running_psalm/issues/ConflictingReferenceConstraint.md
vendored
Normal file
39
vendor/vimeo/psalm/docs/running_psalm/issues/ConflictingReferenceConstraint.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# ConflictingReferenceConstraint
|
||||
|
||||
Emitted when a by-ref variable is set in two different branches of an if to different types.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/** @var int */
|
||||
private $foo;
|
||||
|
||||
public function __construct(int &$foo) {
|
||||
$this->foo = &$foo;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
/** @var string */
|
||||
private $bar;
|
||||
|
||||
public function __construct(string &$bar) {
|
||||
$this->bar = &$bar;
|
||||
}
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$v = 5;
|
||||
$c = (new A($v)); // $v is constrained to an int
|
||||
} else {
|
||||
$v = "hello";
|
||||
$c = (new B($v)); // $v is constrained to a string
|
||||
}
|
||||
|
||||
$v = 8;
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
Psalm doesn't understand what the type of `$c` should be
|
||||
15
vendor/vimeo/psalm/docs/running_psalm/issues/ConstantDeclarationInTrait.md
vendored
Normal file
15
vendor/vimeo/psalm/docs/running_psalm/issues/ConstantDeclarationInTrait.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# ConstantDeclarationInTrait
|
||||
|
||||
Emitted when a trait declares a constant in PHP <8.2.0
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
trait A {
|
||||
const B = 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
A fatal error will be thrown.
|
||||
17
vendor/vimeo/psalm/docs/running_psalm/issues/ConstructorSignatureMismatch.md
vendored
Normal file
17
vendor/vimeo/psalm/docs/running_psalm/issues/ConstructorSignatureMismatch.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# ConstructorSignatureMismatch
|
||||
|
||||
Emitted when a constructor parameter differs from a parent constructor parameter, or if there are fewer parameters than the parent constructor AND where the parent class has a `@psalm-consistent-constructor` annotation.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A {
|
||||
public function __construct(int $i) {}
|
||||
}
|
||||
class B extends A {
|
||||
public function __construct(string $s) {}
|
||||
}
|
||||
```
|
||||
14
vendor/vimeo/psalm/docs/running_psalm/issues/ContinueOutsideLoop.md
vendored
Normal file
14
vendor/vimeo/psalm/docs/running_psalm/issues/ContinueOutsideLoop.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# ContinueOutsideLoop
|
||||
|
||||
Emitted when encountering a `continue` statement outside a loop context.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$a = 5;
|
||||
continue;
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The code won't compile in PHP 5.6 and above.
|
||||
19
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedClass.md
vendored
Normal file
19
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedClass.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# DeprecatedClass
|
||||
|
||||
Emitted when referring to a deprecated class:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @deprecated */
|
||||
class A {}
|
||||
new A();
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated class.
|
||||
29
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedConstant.md
vendored
Normal file
29
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedConstant.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# DeprecatedConstant
|
||||
|
||||
Emitted when referring to a deprecated constant or enum case:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/** @deprecated */
|
||||
const FOO = 'foo';
|
||||
}
|
||||
|
||||
echo A::FOO;
|
||||
|
||||
enum B {
|
||||
/** @deprecated */
|
||||
case B;
|
||||
}
|
||||
|
||||
echo B::B;
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated constant or enum case
|
||||
19
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedFunction.md
vendored
Normal file
19
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedFunction.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# DeprecatedFunction
|
||||
|
||||
Emitted when calling a deprecated function:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @deprecated */
|
||||
function foo() : void {}
|
||||
foo();
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated function.
|
||||
20
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedInterface.md
vendored
Normal file
20
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedInterface.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# DeprecatedInterface
|
||||
|
||||
Emitted when referring to a deprecated interface
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @deprecated */
|
||||
interface I {}
|
||||
|
||||
class A implements I {}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated interface.
|
||||
21
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedMethod.md
vendored
Normal file
21
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedMethod.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# DeprecatedMethod
|
||||
|
||||
Emitted when calling a deprecated method on a given class:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/** @deprecated */
|
||||
public function foo() : void {}
|
||||
}
|
||||
(new A())->foo();
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated method.
|
||||
24
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedProperty.md
vendored
Normal file
24
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedProperty.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# DeprecatedProperty
|
||||
|
||||
Emitted when getting/setting a deprecated property of a given class
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/**
|
||||
* @deprecated
|
||||
* @var ?string
|
||||
*/
|
||||
public $foo;
|
||||
}
|
||||
(new A())->foo = 5;
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated property.
|
||||
21
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedTrait.md
vendored
Normal file
21
vendor/vimeo/psalm/docs/running_psalm/issues/DeprecatedTrait.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# DeprecatedTrait
|
||||
|
||||
Emitted when referring to a deprecated trait:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @deprecated */
|
||||
trait T {}
|
||||
class A {
|
||||
use T;
|
||||
}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The `@deprecated` tag is normally indicative of code that will stop working in the near future.
|
||||
|
||||
## How to fix
|
||||
|
||||
Don’t use the deprecated trait.
|
||||
12
vendor/vimeo/psalm/docs/running_psalm/issues/DirectConstructorCall.md
vendored
Normal file
12
vendor/vimeo/psalm/docs/running_psalm/issues/DirectConstructorCall.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# DirectConstructorCall
|
||||
|
||||
Emitted when `__construct()` is called directly as a method. Constructors are supposed to be called implicitely, as a result of `new ClassName` statement.
|
||||
|
||||
```php
|
||||
<?php
|
||||
class A {
|
||||
public function __construct() {}
|
||||
}
|
||||
$a = new A;
|
||||
$a->__construct(); // wrong
|
||||
```
|
||||
40
vendor/vimeo/psalm/docs/running_psalm/issues/DocblockTypeContradiction.md
vendored
Normal file
40
vendor/vimeo/psalm/docs/running_psalm/issues/DocblockTypeContradiction.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# DocblockTypeContradiction
|
||||
|
||||
Emitted when conditional doesn't make sense given the docblock types supplied.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @param string $s
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function foo($s) {
|
||||
if ($s === null) {
|
||||
throw new \Exception('Bad input');
|
||||
}
|
||||
|
||||
echo $s;
|
||||
}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
This can sometimes point to a flaw in either your docblock types, or some unnecessary runtime checks in an environment where all types can be checked by Psalm, without the need for additional runtime checks.
|
||||
|
||||
## How to fix
|
||||
|
||||
A lot of old PHP code is set up to prevent unexpected errors with checks like the one above.
|
||||
|
||||
As you migrate your code to newer versions of PHP you can also use stricter type-hints:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function foo(string $s) : void {
|
||||
echo $s;
|
||||
}
|
||||
```
|
||||
|
||||
Since it's impossible for `$s` to be anything but a `string` inside the function, the null check can be removed.
|
||||
40
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateArrayKey.md
vendored
Normal file
40
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateArrayKey.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# DuplicateArrayKey
|
||||
|
||||
Emitted when an array has a key more than once
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$arr = [
|
||||
'a' => 'one',
|
||||
'b' => 'two',
|
||||
'c' => 'this text will be overwritten by the next line',
|
||||
'c' => 'three',
|
||||
];
|
||||
```
|
||||
|
||||
This can be caused by variadic arguments if `@no-named-arguments` is not specified:
|
||||
|
||||
```php
|
||||
<?php
|
||||
function foo($bar, ...$baz): array
|
||||
{
|
||||
return [$bar, ...$baz]; // $baz is array<array-key, mixed> since it can have named arguments
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
Remove the offending duplicates:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$arr = [
|
||||
'a' => 'one',
|
||||
'b' => 'two',
|
||||
'c' => 'three',
|
||||
];
|
||||
```
|
||||
|
||||
The first matching `'c'` key was removed to prevent a change in behaviour (any new duplicate keys overwrite the values of previous ones).
|
||||
32
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateClass.md
vendored
Normal file
32
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateClass.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# DuplicateClass
|
||||
|
||||
Emitted when a class is defined twice
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
class A {}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The above code won’t compile.
|
||||
|
||||
PHP does allow you to define a class conditionally:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
if (rand(0, 1)) {
|
||||
class A {
|
||||
public function __construct(string $s) {}
|
||||
}
|
||||
} else {
|
||||
class A {
|
||||
public function __construct(object $o) {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
But Psalm _really_ doesn't want you to use this pattern – it's impossible for Psalm to know (without using reflection) which class is getting used.
|
||||
16
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateConstant.md
vendored
Normal file
16
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateConstant.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# DuplicateConstant
|
||||
|
||||
Emitted when a constant is defined twice in a single class or when there's a
|
||||
clash between a constant and an enum case.
|
||||
|
||||
```php
|
||||
<?php
|
||||
class C {
|
||||
public const A = 1;
|
||||
public const A = 2;
|
||||
}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The above code won’t compile.
|
||||
27
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateEnumCase.md
vendored
Normal file
27
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateEnumCase.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# DuplicateEnumCase
|
||||
|
||||
Emitted when enum has duplicate cases.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
enum Status
|
||||
{
|
||||
case Open;
|
||||
case Open;
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
Remove or rename the offending duplicates.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
enum Status
|
||||
{
|
||||
case Open;
|
||||
case Closed;
|
||||
}
|
||||
```
|
||||
27
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateEnumCaseValue.md
vendored
Normal file
27
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateEnumCaseValue.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# DuplicateEnumCaseValue
|
||||
|
||||
Emitted when a backed enum has duplicate case values.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
enum Status: string
|
||||
{
|
||||
case Open = "open";
|
||||
case Closed = "open";
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
Change case values so that there are no duplicates.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
enum Status: string
|
||||
{
|
||||
case Open = "open";
|
||||
case Closed = "closed";
|
||||
}
|
||||
```
|
||||
11
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateFunction.md
vendored
Normal file
11
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateFunction.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# DuplicateFunction
|
||||
|
||||
Emitted when a function is defined twice
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function foo() : void {}
|
||||
function bar() : void {}
|
||||
function foo() : void {}
|
||||
```
|
||||
12
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateMethod.md
vendored
Normal file
12
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateMethod.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# DuplicateMethod
|
||||
|
||||
Emitted when a method is defined twice
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public function foo() {}
|
||||
public function foo() {}
|
||||
}
|
||||
```
|
||||
13
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateParam.md
vendored
Normal file
13
vendor/vimeo/psalm/docs/running_psalm/issues/DuplicateParam.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# DuplicateParam
|
||||
|
||||
Emitted when a function has a param defined twice
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function foo(int $b, string $b) {}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The above code produces a fatal error in PHP.
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/EmptyArrayAccess.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/EmptyArrayAccess.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# EmptyArrayAccess
|
||||
|
||||
Emitted when attempting to access a value on an empty array
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$a = [];
|
||||
$b = $a[0];
|
||||
```
|
||||
20
vendor/vimeo/psalm/docs/running_psalm/issues/ExtensionRequirementViolation.md
vendored
Normal file
20
vendor/vimeo/psalm/docs/running_psalm/issues/ExtensionRequirementViolation.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# ExtensionRequirementViolation
|
||||
|
||||
Emitted when a using class of a trait does not extend the class specified using `@psalm-require-extends`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A { }
|
||||
|
||||
/**
|
||||
* @psalm-require-extends A
|
||||
*/
|
||||
trait T { }
|
||||
|
||||
class B {
|
||||
// ExtensionRequirementViolation is emitted, as T requires
|
||||
// the using class B to extend A, which is not the case
|
||||
use T;
|
||||
}
|
||||
```
|
||||
45
vendor/vimeo/psalm/docs/running_psalm/issues/FalsableReturnStatement.md
vendored
Normal file
45
vendor/vimeo/psalm/docs/running_psalm/issues/FalsableReturnStatement.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# FalsableReturnStatement
|
||||
|
||||
Emitted if a return statement contains a false value, but the function return type does not allow false
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function getCommaPosition(string $a) : int {
|
||||
return strpos($a, ',');
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
You can add a specific check for false:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function getCommaPosition(string $a) : int {
|
||||
$pos = strpos($a, ',');
|
||||
|
||||
if ($pos === false) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return $pos;
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively you may choose to throw an exception:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function getCommaPosition(string $a) : int {
|
||||
$pos = strpos($a, ',');
|
||||
|
||||
if ($pos === false) {
|
||||
throw new Exception('This is unexpected');
|
||||
}
|
||||
|
||||
return $pos;
|
||||
}
|
||||
```
|
||||
13
vendor/vimeo/psalm/docs/running_psalm/issues/FalseOperand.md
vendored
Normal file
13
vendor/vimeo/psalm/docs/running_psalm/issues/FalseOperand.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# FalseOperand
|
||||
|
||||
Emitted when using `false` as part of an operation (e.g. `+`, `.`, `^` etc.)
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
echo 5 . false;
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
`false` does not make sense in these operations
|
||||
23
vendor/vimeo/psalm/docs/running_psalm/issues/ForbiddenCode.md
vendored
Normal file
23
vendor/vimeo/psalm/docs/running_psalm/issues/ForbiddenCode.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# ForbiddenCode
|
||||
|
||||
Emitted when Psalm encounters a var_dump, exec or similar expression that may make your code more vulnerable
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
var_dump("bah");
|
||||
```
|
||||
|
||||
This functions list can be extended by configuring `forbiddenFunctions` in `psalm.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<!-- other configs -->
|
||||
|
||||
<forbiddenFunctions>
|
||||
<function name="dd"/>
|
||||
<function name="var_dump"/>
|
||||
</forbiddenFunctions>
|
||||
</psalm>
|
||||
```
|
||||
35
vendor/vimeo/psalm/docs/running_psalm/issues/IfThisIsMismatch.md
vendored
Normal file
35
vendor/vimeo/psalm/docs/running_psalm/issues/IfThisIsMismatch.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# IfThisIsMismatch
|
||||
|
||||
Emitted when the type in `@psalm-if-this-is` annotation cannot be contained by the object's actual type.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class a {
|
||||
/**
|
||||
* @var T
|
||||
*/
|
||||
private $data;
|
||||
/**
|
||||
* @param T $data
|
||||
*/
|
||||
public function __construct($data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
/**
|
||||
* @psalm-if-this-is a<int>
|
||||
*/
|
||||
public function test(): void {
|
||||
}
|
||||
}
|
||||
|
||||
$i = new a(123);
|
||||
$i->test();
|
||||
|
||||
$i = new a("test");
|
||||
// IfThisIsMismatch - Class is not a<int> as required by psalm-if-this-is
|
||||
$i->test();
|
||||
```
|
||||
22
vendor/vimeo/psalm/docs/running_psalm/issues/ImplementationRequirementViolation.md
vendored
Normal file
22
vendor/vimeo/psalm/docs/running_psalm/issues/ImplementationRequirementViolation.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# ImplementationRequirementViolation
|
||||
|
||||
Emitted when a using class of a trait does not implement all interfaces specified using `@psalm-require-implements`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
interface A { }
|
||||
interface B { }
|
||||
|
||||
/**
|
||||
* @psalm-require-implements A
|
||||
* @psalm-require-implements B
|
||||
*/
|
||||
trait T { }
|
||||
|
||||
class C {
|
||||
// ImplementationRequirementViolation is emitted, as T requires
|
||||
// the using class C to implement A and B, which is not the case
|
||||
use T;
|
||||
}
|
||||
```
|
||||
35
vendor/vimeo/psalm/docs/running_psalm/issues/ImplementedParamTypeMismatch.md
vendored
Normal file
35
vendor/vimeo/psalm/docs/running_psalm/issues/ImplementedParamTypeMismatch.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# ImplementedParamTypeMismatch
|
||||
|
||||
Emitted when a class that inherits another, or implements an interface, has a docblock param type that's entirely different to the parent.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class D {
|
||||
/** @param string $a */
|
||||
public function foo($a): void {}
|
||||
}
|
||||
|
||||
class E extends D {
|
||||
/** @param int $a */
|
||||
public function foo($a): void {}
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
Make sure to respect the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) – any method that overrides a parent method must accept all the same arguments as its parent method.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class D {
|
||||
/** @param string $a */
|
||||
public function foo($a): void {}
|
||||
}
|
||||
|
||||
class E extends D {
|
||||
/** @param string|int $a */
|
||||
public function foo($a): void {}
|
||||
}
|
||||
```
|
||||
43
vendor/vimeo/psalm/docs/running_psalm/issues/ImplementedReturnTypeMismatch.md
vendored
Normal file
43
vendor/vimeo/psalm/docs/running_psalm/issues/ImplementedReturnTypeMismatch.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# ImplementedReturnTypeMismatch
|
||||
|
||||
Emitted when a class that inherits another, or implements an interface, has a docblock return type that's entirely different to the parent.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/** @return bool */
|
||||
public function foo() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
class B extends A {
|
||||
/** @return string */
|
||||
public function foo() {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
Make sure to respect the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) – any method that overrides a parent method must return a subtype of the parent method.
|
||||
|
||||
In the above case, that means adding the child return type to the parent one.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/** @return bool|string */
|
||||
public function foo() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
class B extends A {
|
||||
/** @return string */
|
||||
public function foo() {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
```
|
||||
29
vendor/vimeo/psalm/docs/running_psalm/issues/ImplicitToStringCast.md
vendored
Normal file
29
vendor/vimeo/psalm/docs/running_psalm/issues/ImplicitToStringCast.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# ImplicitToStringCast
|
||||
|
||||
Emitted when implicitly converting an object with a `__toString` method to a string
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public function __toString() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
function takesString(string $s) : void {}
|
||||
|
||||
takesString(new A);
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
You can add an explicit string cast:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
...
|
||||
|
||||
takesString((string) new A);
|
||||
```
|
||||
30
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureByReferenceAssignment.md
vendored
Normal file
30
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureByReferenceAssignment.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# ImpureByReferenceAssignment
|
||||
|
||||
Emitted when assigning a passed-by-reference variable inside a function or method marked as mutation-free.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
function foo(string &$a): string {
|
||||
$a = "B";
|
||||
return $a;
|
||||
}
|
||||
```
|
||||
|
||||
## How to fix
|
||||
|
||||
Just remove the mutating assignment:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
function foo(string &$a): string {
|
||||
return $a;
|
||||
}
|
||||
```
|
||||
23
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureFunctionCall.md
vendored
Normal file
23
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureFunctionCall.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# ImpureFunctionCall
|
||||
|
||||
Emitted when calling an impure function from a function or method marked as pure.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function impure(array $a) : array {
|
||||
/** @var int */
|
||||
static $i = 0;
|
||||
|
||||
++$i;
|
||||
|
||||
$a[$i] = 1;
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
/** @psalm-pure */
|
||||
function filterOdd(array $a) : void {
|
||||
impure($a);
|
||||
}
|
||||
```
|
||||
26
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureMethodCall.md
vendored
Normal file
26
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureMethodCall.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# ImpureMethodCall
|
||||
|
||||
Emitted when calling an impure method from a function or method marked as pure.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public int $a = 5;
|
||||
|
||||
public function foo() : void {
|
||||
$this->a++;
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-pure */
|
||||
function filterOdd(int $i, A $a) : ?int {
|
||||
$a->foo();
|
||||
|
||||
if ($i % 2 === 0 || $a->a === 2) {
|
||||
return $i;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
18
vendor/vimeo/psalm/docs/running_psalm/issues/ImpurePropertyAssignment.md
vendored
Normal file
18
vendor/vimeo/psalm/docs/running_psalm/issues/ImpurePropertyAssignment.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# ImpurePropertyAssignment
|
||||
|
||||
Emitted when updating a property value from a function or method marked as pure.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public int $a = 5;
|
||||
}
|
||||
|
||||
/** @psalm-pure */
|
||||
function foo(int $i, A $a) : int {
|
||||
$a->a = $i;
|
||||
|
||||
return $i;
|
||||
}
|
||||
```
|
||||
16
vendor/vimeo/psalm/docs/running_psalm/issues/ImpurePropertyFetch.md
vendored
Normal file
16
vendor/vimeo/psalm/docs/running_psalm/issues/ImpurePropertyFetch.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# ImpurePropertyFetch
|
||||
|
||||
Emitted when fetching a property value inside a function or method marked as pure.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public int $a = 5;
|
||||
}
|
||||
|
||||
/** @psalm-pure */
|
||||
function foo(int $i, A $a) : int {
|
||||
return $i + $a->a;
|
||||
}
|
||||
```
|
||||
18
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureStaticProperty.md
vendored
Normal file
18
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureStaticProperty.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# ImpureStaticProperty
|
||||
|
||||
Emitted when attempting to use a static property from a function or method marked as pure
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class ValueHolder {
|
||||
public static ?string $value = null;
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function get(): ?string {
|
||||
return self::$value;
|
||||
}
|
||||
}
|
||||
```
|
||||
15
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureStaticVariable.md
vendored
Normal file
15
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureStaticVariable.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# ImpureStaticVariable
|
||||
|
||||
Emitted when attempting to use a static variable from a function or method marked as pure
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @psalm-pure */
|
||||
function addCumulative(int $left) : int {
|
||||
/** @var int */
|
||||
static $i = 0;
|
||||
$i += $left;
|
||||
return $left;
|
||||
}
|
||||
```
|
||||
18
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureVariable.md
vendored
Normal file
18
vendor/vimeo/psalm/docs/running_psalm/issues/ImpureVariable.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# ImpureVariable
|
||||
|
||||
Emitted when referencing an impure or possibly-impure variable from a pure context.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public int $a = 5;
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public function foo() : self {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
```
|
||||
12
vendor/vimeo/psalm/docs/running_psalm/issues/InaccessibleClassConstant.md
vendored
Normal file
12
vendor/vimeo/psalm/docs/running_psalm/issues/InaccessibleClassConstant.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# InaccessibleClassConstant
|
||||
|
||||
Emitted when a public/private class constant is not accessible from the calling context
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
protected const FOO = 'FOO';
|
||||
}
|
||||
echo A::FOO;
|
||||
```
|
||||
12
vendor/vimeo/psalm/docs/running_psalm/issues/InaccessibleMethod.md
vendored
Normal file
12
vendor/vimeo/psalm/docs/running_psalm/issues/InaccessibleMethod.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# InaccessibleMethod
|
||||
|
||||
Emitted when attempting to access a protected/private method from outside its available scope
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
protected function foo() : void {}
|
||||
}
|
||||
echo (new A)->foo();
|
||||
```
|
||||
13
vendor/vimeo/psalm/docs/running_psalm/issues/InaccessibleProperty.md
vendored
Normal file
13
vendor/vimeo/psalm/docs/running_psalm/issues/InaccessibleProperty.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# InaccessibleProperty
|
||||
|
||||
Emitted when attempting to access a protected/private property from outside its available scope
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {
|
||||
/** @return string */
|
||||
protected $foo;
|
||||
}
|
||||
echo (new A)->foo;
|
||||
```
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/InterfaceInstantiation.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/InterfaceInstantiation.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# InterfaceInstantiation
|
||||
|
||||
Emitted when an attempt is made to instantiate an interface:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
interface I {}
|
||||
new I();
|
||||
```
|
||||
23
vendor/vimeo/psalm/docs/running_psalm/issues/InternalClass.md
vendored
Normal file
23
vendor/vimeo/psalm/docs/running_psalm/issues/InternalClass.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# InternalClass
|
||||
|
||||
Emitted when attempting to access a class marked as internal an unrelated namespace or class, or attempting
|
||||
to access a class marked as psalm-internal to a different namespace.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace A {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Foo { }
|
||||
}
|
||||
|
||||
namespace B {
|
||||
class Bat {
|
||||
public function batBat(): void {
|
||||
$a = new \A\Foo();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
25
vendor/vimeo/psalm/docs/running_psalm/issues/InternalMethod.md
vendored
Normal file
25
vendor/vimeo/psalm/docs/running_psalm/issues/InternalMethod.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# InternalMethod
|
||||
|
||||
Emitted when attempting to access a method marked as internal an unrelated namespace or class, or attempting
|
||||
to access a method marked as psalm-internal to a different namespace.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace A {
|
||||
class Foo {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function barBar(): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace B {
|
||||
class Bat {
|
||||
public function batBat(): void {
|
||||
\A\Foo::barBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
26
vendor/vimeo/psalm/docs/running_psalm/issues/InternalProperty.md
vendored
Normal file
26
vendor/vimeo/psalm/docs/running_psalm/issues/InternalProperty.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# InternalProperty
|
||||
|
||||
Emitted when attempting to access a property marked as internal from an unrelated namespace or class, or attempting
|
||||
to access a property marked as psalm-internal to a different namespace.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace A {
|
||||
class Foo {
|
||||
/**
|
||||
* @internal
|
||||
* @var ?int
|
||||
*/
|
||||
public $foo;
|
||||
}
|
||||
}
|
||||
|
||||
namespace B {
|
||||
class Bat {
|
||||
public function batBat() : void {
|
||||
echo (new \A\Foo)->foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
40
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArgument.md
vendored
Normal file
40
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArgument.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# InvalidArgument
|
||||
|
||||
Emitted when a supplied function/method argument is incompatible with the method signature or docblock one.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
|
||||
function foo(A $a) : void {}
|
||||
|
||||
/**
|
||||
* @param string $s
|
||||
*/
|
||||
function callFoo($s) : void {
|
||||
foo($s);
|
||||
}
|
||||
```
|
||||
|
||||
## Why it’s bad
|
||||
|
||||
Calling functions with incorrect values will cause a fatal error at runtime.
|
||||
|
||||
## How to fix
|
||||
|
||||
Sometimes this message can just be the result of an incorrect docblock.
|
||||
|
||||
You can fix by correcting the docblock, or converting to a function signature:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
|
||||
function foo(A $a) : void {}
|
||||
|
||||
function callFoo(A $a) : void {
|
||||
foo($a);
|
||||
}
|
||||
```
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArrayAccess.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArrayAccess.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# InvalidArrayAccess
|
||||
|
||||
Emitted when attempting to access an array offset on a value that does not permit it
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$arr = 5;
|
||||
echo $arr[0];
|
||||
```
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArrayAssignment.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArrayAssignment.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# InvalidArrayAssignment
|
||||
|
||||
Emitted when attempting to assign a value on a non-array
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$arr = 5;
|
||||
$arr[0] = 3;
|
||||
```
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArrayOffset.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidArrayOffset.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# InvalidArrayOffset
|
||||
|
||||
Emitted when attempting to access an array using a value that's not a valid offset for that array
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$a = [5, 20, 18];
|
||||
echo $a["hello"];
|
||||
```
|
||||
16
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidAttribute.md
vendored
Normal file
16
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidAttribute.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# InvalidAttribute
|
||||
|
||||
Emitted when using an attribute on an element that doesn't match the attribute's target
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Foo;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Table {
|
||||
public function __construct(public string $name) {}
|
||||
}
|
||||
|
||||
#[Table("videos")]
|
||||
function foo() : void {}
|
||||
```
|
||||
11
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidCast.md
vendored
Normal file
11
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidCast.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# InvalidCast
|
||||
|
||||
Emitted when attempting to cast a value that's not castable
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
$a = new A();
|
||||
$b = (string)$a;
|
||||
```
|
||||
13
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidCatch.md
vendored
Normal file
13
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidCatch.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# InvalidCatch
|
||||
|
||||
Emitted when trying to catch a class/interface that doesn't extend `Exception` or implement `Throwable`
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class A {}
|
||||
try {
|
||||
$worked = true;
|
||||
}
|
||||
catch (A $e) {}
|
||||
```
|
||||
26
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidClass.md
vendored
Normal file
26
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidClass.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# InvalidClass
|
||||
|
||||
Emitted when referencing a class with the wrong casing
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Foo {}
|
||||
(new foo());
|
||||
```
|
||||
|
||||
Could also be an issue in the namespace even if the class has the correct casing
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace OneTwo {
|
||||
class Three {}
|
||||
}
|
||||
|
||||
namespace {
|
||||
use Onetwo\Three;
|
||||
// ^ ("t" instead of "T")
|
||||
|
||||
$three = new Three();
|
||||
}
|
||||
```
|
||||
28
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidClassConstantType.md
vendored
Normal file
28
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidClassConstantType.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# InvalidClassConstantType
|
||||
|
||||
Emitted when a constant type in a child does not satisfy the type in the parent.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Foo
|
||||
{
|
||||
/** @var int<1,max> */
|
||||
public const CONSTANT = 3;
|
||||
|
||||
public static function bar(): array
|
||||
{
|
||||
return str_split("foobar", static::CONSTANT);
|
||||
}
|
||||
}
|
||||
|
||||
class Bar extends Foo
|
||||
{
|
||||
/** @var int<min,-1> */
|
||||
public const CONSTANT = -1;
|
||||
}
|
||||
|
||||
Bar::bar(); // Error: str_split argument 2 must be greater than 0
|
||||
```
|
||||
|
||||
This issue will always show up when overriding a constant that doesn't have a docblock type. Psalm will infer the most specific type for the constant that it can, you have to add a type annotation to tell it what type constraint you wish to be applied. Otherwise Psalm has no way of telling if you mean for the constant to be a literal `1`, `int<1, max>`, `int`, `numeric`, etc.
|
||||
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidClone.md
vendored
Normal file
10
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidClone.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# InvalidClone
|
||||
|
||||
Emitted when trying to clone a value that's not cloneable
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$a = "hello";
|
||||
$b = clone $a;
|
||||
```
|
||||
12
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidConstantAssignmentValue.md
vendored
Normal file
12
vendor/vimeo/psalm/docs/running_psalm/issues/InvalidConstantAssignmentValue.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# InvalidConstantAssignmentValue
|
||||
|
||||
Emitted when attempting to assign a value to a class constant that cannot contain that type.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
/** @var int */
|
||||
public const BAR = "bar";
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user