1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00
Commit Graph

315 Commits

Author SHA1 Message Date
Corey Taylor
cdbd71ba8b Add DatePeriod::INCLUDE_END_DATE option 2023-02-03 13:37:45 -06:00
orklah
54cd529642
Merge pull request #9170 from mmcev106/filter-var-pure
Added @psalm-pure to filter_var()
2023-02-02 23:39:06 +01:00
Mark McEver
b5781c34e8 Fixed a case where the conditional taint, specialize, & flow features were not playing nicely together 2023-02-01 15:41:52 -06:00
Mark McEver
bb2cf1f30a Added @psalm-pure to filter_var() 2023-01-23 15:09:29 -06:00
orklah
a4d1b0691d
Merge pull request #9085 from BenMorel/newInstanceArgs
Fix ReflectionClass::newInstanceArgs() with named parameters
2023-01-19 23:08:21 +01:00
Jack Worman
787b9ae687 Fix dom_import_simplexml() 2023-01-18 18:31:54 -05:00
Benjamin Morel
951f0827a3 Infer getDefaultValueConstantName() type when isDefaultValueConstant() is true 2023-01-15 19:11:10 +01:00
orklah
f415ad56fc
Merge pull request #9073 from therealryanbonham/9066_fix_get_headers_signature
Update Call Maps and Signature for get_headers
2023-01-09 18:41:14 +01:00
Alies Lapatsin
63f328cb4c Unify syntax for redis.phpstub 2023-01-09 14:12:39 +01:00
Benjamin Morel
a669c89074 Fix ReflectionClass::newInstanceArgs() 2023-01-08 23:58:14 +01:00
therealryanbonham
e4e52da218
Merge branch 'master' into 9066_fix_get_headers_signature 2023-01-07 19:27:01 -05:00
orklah
d64023548c
Merge pull request #9067 from weirdan/backed-enum-cases
fix `I::cases()` where `interface I extends BackedEnum`
2023-01-07 21:05:35 +01:00
Ryan Bonham
cb204edcdd
Correct Stubs for PHP 7 and PHP 8 2023-01-07 12:38:49 -05:00
Ryan Bonham
0d360419b7
Update Call Maps and Signature for get_headers 2023-01-06 19:24:00 -05:00
Rudolph Gottesheim
18e8dd9da1 Narrow down the return type of DOMXPath::query() 2023-01-06 16:19:48 +01:00
Bruce Weirdan
afb8c8891b
fix I::cases() where interface I extends BackedEnum
Fixes vimeo/psalm#9065

Inheritance in stubs seems to be broken
2023-01-05 21:35:47 -04:00
Jaroslav Hanslík
65cf669bff
Fix ReflectionClass stub 2023-01-03 23:55:59 +01:00
orklah
e527c68706
Merge pull request #9051 from kukulich/patch-4
Fix `ReflectionClass::getTraitNames()` stub
2023-01-03 23:17:48 +01:00
orklah
f978594157
Merge pull request #9052 from kukulich/patch-5
Fix `ReflectionMethod::getModifiers()` stub
2023-01-03 23:17:09 +01:00
Jaroslav Hanslík
cbea438a23
Fix ReflectionFunctionAbstract::getExtensionName() stub 2023-01-03 22:16:58 +01:00
Jaroslav Hanslík
4f8f233e76
Fix ReflectionMethod::getModifiers() stub 2023-01-03 22:08:36 +01:00
Jaroslav Hanslík
2310c1361d
Fix ReflectionClass::getTraitNames() stub 2023-01-03 21:46:47 +01:00
orklah
3560ef7717
Merge pull request #9046 from kukulich/patch-1
Fix `ReflectionClass::getTraitAliases()` stub
2023-01-03 21:05:17 +01:00
orklah
7cde3c33d2
Merge pull request #9048 from kukulich/patch-3
Fix `ReflectionClass::getReflectionConstants()` stub
2023-01-03 21:04:49 +01:00
Jaroslav Hanslík
9ebefcef7d
Fix ReflectionClass::getReflectionConstants() stub 2023-01-03 18:20:08 +01:00
Jaroslav Hanslík
865a824818
Fix ReflectionClass::getModifiers() stub 2023-01-03 18:15:56 +01:00
Jaroslav Hanslík
415ef8fc75
Fix ReflectionClass::getTraitAliases() stub 2023-01-03 17:58:53 +01:00
orklah
790c30959d
Merge pull request #9016 from Ocramius/feature/#5039-more-refined-types-for-explode-core-function
Refined `explode()` types
2022-12-29 10:23:18 +01:00
Mark McEver
53c3f1ebb3 Prevent other DB escaping functions from escaping non-sql taints 2022-12-28 14:19:01 -06:00
Mark McEver
69f31dcd4a Prevent mysqli escaping functions from escaping non-sql taints 2022-12-28 13:39:01 -06:00
Marco Pivetta
c0c0116809 Using list{0: string, 1?: string} syntax for more precise array key types
Thanks to @orklah's feedback, the `explode()` return type is now much more precise too.

Ref: https://github.com/vimeo/psalm/pull/9016#discussion_r1058458616
2022-12-28 17:48:33 +01:00
Marco Pivetta
bfded43614 Ensure that explode($d, lowercase-string) produces list<lowercase-string> types
This specific distinction seems to be very important for Psalm, as `explode()` and
`lowercase-string` are used aggressively across the codebase.

Also, this change expands the baseline by a few entries, since some of the code locations
instide Psalm itself have un-checked list destructuring operations, as well as array
access calls on potentially undefined array keys produced by `explode()`, which were
previously just `list<string>`, and are now `array{0: string, 1?: string}`, which is
a bit more precise.
2022-12-28 17:26:25 +01:00
Marco Pivetta
04999b172a Refined explode() types
Fixes #5039

This patch removes the need for a custom function return type
provider for `explode()`, and instead replaces all that with a single
stub for the `explode()` function, which provides types for some of
the most common `$limit` input values.

With this change, the `$delimiter` is enforced to be a `non-empty-string`,
which will lead to downstream consumers having to adjust some code accordingly,
but that shouldn't affect the most common scenario of exploding a string
based with a constant `literal-string` delimiter, which most PHP devs tend to do.

This change didn't come with an accompanying test, since that would be a bit
wasteful, but it was verified locally with following script:

```php
<?php

$possible0  = explode(',', 'hello, world', -100);
$possible1  = explode(',', 'hello, world', -1);
$possible2  = explode(',', 'hello, world', 0);
$possible3  = explode(',', 'hello, world', 1);
$possible4  = explode(',', 'hello, world', 2);
$possible5  = explode(',', 'hello, world', 3);
$possible6  = explode(',', 'hello, world', 4);
try {
    $impossible1 = explode('', '', -1);
} catch (Throwable $impossible1) {}

$traced = [$possible0, $possible1, $possible2, $possible3, $possible4, $possible5, $possible6, $impossible1];

/** @psalm-trace $traced */

var_dump($traced);

return $traced;
```

Running psalm locally, this produces:

```
psalm on  feature/#5039-more-refined-types-for-explode-core-function [?] via 🐘 v8.1.13 via ❄️  impure (nix-shell)
❯ ./psalm --no-cache explode.php
Target PHP version: 7.4 (inferred from composer.json) Extensions enabled: dom, simplexml (unsupported extensions: ctype, json, libxml, mbstring, tokenizer)
Scanning files...
Analyzing files...

░

To whom it may concern: Psalm cannot detect unused classes, methods and properties
when analyzing individual files and folders. Run on the full project to enable
complete unused code detection.

ERROR: InvalidArgument - explode.php:11:28 - Argument 1 of explode expects non-empty-string, but '' provided (see https://psalm.dev/004)
    $impossible1 = explode('', '', -1);

ERROR: PossiblyUndefinedGlobalVariable - explode.php:14:96 - Possibly undefined global variable $impossible1 defined in try block (see https://psalm.dev/126)
$traced = [$possible0, $possible1, $possible2, $possible3, $possible4, $possible5, $possible6, $impossible1];

ERROR: ForbiddenCode - explode.php:18:1 - Unsafe var_dump (see https://psalm.dev/002)
/** @psalm-trace $traced */

var_dump($traced);

ERROR: Trace - explode.php:18:1 - $traced: list{0: array<never, never>, 1: non-empty-list<string>, 2: list{string}, 3: list{string}, 4: array{0: string, 1?: string}, 5: array{0: string, 1?: string, 2?: string}, 6: non-empty-list<string>, 7?: Throwable|non-empty-list<string>} (see https://psalm.dev/224)
/** @psalm-trace $traced */

var_dump($traced);

------------------------------
4 errors found
------------------------------

Checks took 6.31 seconds and used 265.386MB of memory
Psalm was unable to infer types in the codebase
```

The actual runtime behavior on PHP 8.x: https://3v4l.org/0NKlW

```
array(8) {
  [0]=>
  array(0) {
  }
  [1]=>
  array(1) {
    [0]=>
    string(5) "hello"
  }
  [2]=>
  array(1) {
    [0]=>
    string(12) "hello, world"
  }
  [3]=>
  array(1) {
    [0]=>
    string(12) "hello, world"
  }
  [4]=>
  array(2) {
    [0]=>
    string(5) "hello"
    [1]=>
    string(6) " world"
  }
  [5]=>
  array(2) {
    [0]=>
    string(5) "hello"
    [1]=>
    string(6) " world"
  }
  [6]=>
  array(2) {
    [0]=>
    string(5) "hello"
    [1]=>
    string(6) " world"
  }
  [7]=>
  object(ValueError)#1 (7) {
    ["message":protected]=>
    string(51) "explode(): Argument #1 ($separator) cannot be empty"
    ["string":"Error":private]=>
    string(0) ""
    ["code":protected]=>
    int(0)
    ["file":protected]=>
    string(9) "/in/0NKlW"
    ["line":protected]=>
    int(11)
    ["trace":"Error":private]=>
    array(1) {
      [0]=>
      array(4) {
        ["file"]=>
        string(9) "/in/0NKlW"
        ["line"]=>
        int(11)
        ["function"]=>
        string(7) "explode"
        ["args"]=>
        array(3) {
          [0]=>
          string(0) ""
          [1]=>
          string(0) ""
          [2]=>
          int(-1)
        }
      }
    }
    ["previous":"Error":private]=>
    NULL
  }
}
```

On PHP 7:

```
Warning: explode(): Empty delimiter in /in/0NKlW on line 11
array(8) {
  [0]=>
  array(0) {
  }
  [1]=>
  array(1) {
    [0]=>
    string(5) "hello"
  }
  [2]=>
  array(1) {
    [0]=>
    string(12) "hello, world"
  }
  [3]=>
  array(1) {
    [0]=>
    string(12) "hello, world"
  }
  [4]=>
  array(2) {
    [0]=>
    string(5) "hello"
    [1]=>
    string(6) " world"
  }
  [5]=>
  array(2) {
    [0]=>
    string(5) "hello"
    [1]=>
    string(6) " world"
  }
  [6]=>
  array(2) {
    [0]=>
    string(5) "hello"
    [1]=>
    string(6) " world"
  }
  [7]=>
  bool(false)
}
```
2022-12-28 17:11:40 +01:00
Jack Worman
8e5904d624 Fix get_object_vars on enums 2022-12-21 22:51:53 -06:00
Jack Worman
e7d4d697ac Reflection class stub updates 2022-12-18 14:35:41 -06:00
Vincent Langlet
50cf28fba1 Fix getParentClass stub 2022-12-18 19:36:13 +01:00
Vincent Langlet
9b5630bea4 Fix ReflectionClass stub 2022-12-18 19:07:41 +01:00
098d5020d0 array_merge_recursive 2022-12-18 14:52:44 +01:00
1dd062a810 Fix #2481 2022-12-18 14:50:51 +01:00
fluffycondor
9a22d682f5 Simplify conditional return 2022-12-18 17:15:24 +06:00
fluffycondor
987981b12a Make sprintf return non-empty-string when arguments non-empty-strings too 2022-12-18 16:53:19 +06:00
Alies Lapatsin
1115cfb980 Merge branch 'master' into php-ext-with-deprecation
# Conflicts:
#	src/Psalm/Config.php
2022-12-12 23:49:01 +01:00
Alies Lapatsin
16ab9f786b Unify a way how to load stubs for extentions 2022-12-10 17:02:47 +01:00
Marco Pivetta
ed2cde1b93 Mark Reflection(Method|Property)#setAccessible() as pure starting from PHP 8.1 onwards
This will highlight unused code.

Ref: https://github.com/php/php-src/pull/5412
Ref: https://wiki.php.net/rfc/make-reflection-setaccessible-no-op
Ref: https://github.com/php/php-src/pull/5411

Example https://3v4l.org/PNeeZ

```php
<?php

class Foo {
    private $bar = 'baz';
    private function taz() { return 'waz'; }
}

//var_dump((new ReflectionProperty(Foo::class, 'bar'))->getValue(new Foo));
//var_dump((new ReflectionMethod(Foo::class, 'taz'))->invoke(new Foo));
```

Produces (starting from PHP 8.1):

```
string(3) "baz"
string(3) "waz"
```
2022-12-07 14:22:15 +01:00
Marco Pivetta
93c5df6bfc Refine ReflectionUnionType and ReflectionIntersectionType for PHP 8.1 and PHP 8.2
* in PHP 8.0, `ReflectionUnionType` is composed on `ReflectionNamedType`s
* in PHP 8.1, `ReflectionIntersectionType` is composed of `ReflectionNamedType`s
* in PHP 8.2, `ReflectionUnionType` is composed of `ReflectionIntersectionType|ReflectionNamedType`s

Slight variations for each PHP version.

As per local testing, this doesn't work yet.

## Local testing setup:
I did some digging to make sure that the stubs work as expected.

Here's what I did to validate this patch locally (since I don't think it can really be fully automated)

## Create a dummy file to verify used symbols

```php
<?php

namespace Testing;

/** @return \ReflectionClass<\stdClass> */
function getAClass(): \ReflectionClass { throw new \Exception('irrelevant'); }
function getAnUnionType(): \ReflectionUnionType { throw new \Exception('irrelevant'); }
function getAnIntersectionType(): \ReflectionIntersectionType { throw new \Exception('irrelevant'); }

// verifying that `getName()` is stubbed in all versions: this should always be a `class-string<\stdClass>`
$name = getAClass()->getName();
// union types should appear starting with PHP 8.0. Starting with PHP 8.2, they allow for intersections.
$unionTypes = getAnUnionType()->getTypes();
// intersection types should appear starting with PHP 8.1
$intersectionTypes = getAnIntersectionType()->getTypes();

$results = [$name, $unionTypes, $intersectionTypes];

/** @psalm-trace $results */ // tracing this will show us the differences between versions
return $results;
```

## Run the script against various `vimeo/psalm` versions

```sh
docker run --rm -ti -v $(pwd):/app -w /app php:7.4 ./psalm --php-version=7.4 --no-cache reflection-test.php | grep Trace

docker run --rm -ti -v $(pwd):/app -w /app php:8.0 ./psalm --php-version=8.0 --no-cache reflection-test.php | grep Trace

docker run --rm -ti -v $(pwd):/app -w /app php:8.1 ./psalm --php-version=8.1 --no-cache reflection-test.php | grep Trace

docker run --rm -ti -v $(pwd):/app -w /app php:8.2.0RC7-cli ./psalm --php-version=8.2 --no-cache reflection-test.php | grep Trace

```

## Evaluate output

```
❯ docker run --rm -ti -v $(pwd):/app -w /app php:7.4 ./psalm --php-version=7.4 --no-cache reflection-test.php | grep Trace
ERROR: Trace - reflection-test.php:20:1 - $results: list{class-string<stdClass>, mixed, mixed} (see https://psalm.dev/224)

❯ docker run --rm -ti -v $(pwd):/app -w /app php:8.0 ./psalm --php-version=8.0 --no-cache reflection-test.php | grep Trace
ERROR: Trace - reflection-test.php:20:1 - $results: list{class-string<stdClass>, non-empty-list<ReflectionNamedType>, mixed} (see https://psalm.dev/224)

❯ docker run --rm -ti -v $(pwd):/app -w /app php:8.1 ./psalm --php-version=8.1 --no-cache reflection-test.php | grep Trace
ERROR: Trace - reflection-test.php:20:1 - $results: list{class-string<stdClass>, non-empty-list<ReflectionNamedType>, non-empty-list<ReflectionNamedType>} (see https://psalm.dev/224)

psalm on  feature/#8720-improve-types-and-purity-for-reflection-symbols [!?] via 🐘 v8.1.13 via ❄️  impure (nix-shell) took 4s
❯ docker run --rm -ti -v $(pwd):/app -w /app php:8.2.0RC7-cli ./psalm --php-version=8.2 --no-cache reflection-test.php | grep Trace
ERROR: Trace - reflection-test.php:20:1 - $results: list{class-string<stdClass>, non-empty-list<ReflectionNamedType>, non-empty-list<ReflectionNamedType>} (see https://psalm.dev/224)
```
2022-12-06 18:26:50 +01:00
Marco Pivetta
79a1a8b26c Removed templated parameters from ReflectionClass#isInstance()
These templates were leading to false positives: assuming
an `object` is given as input, the inferred return
type would always have been `true`, which is obviously
not valid.

Removing them is the healthier alternative, for now.

Ref: https://github.com/vimeo/psalm/pull/8722#discussion_r1027102713
2022-12-06 11:21:09 +01:00
Marco Pivetta
d9a0cc5311 Prevent usage of callable objects in ReflectionFunction::__construct()
As per @weirdan's feedback, we can prevent
the usage of `object` instances that
implement `__invoke()`, as well as `array`
callables, by declaring the ctor argument of
`ReflectionFunction` to be either a real `Closure`,
or a `callable-string`.

While this may not be 100% of scenarios, it is a
healthy way to identify errors in userland.

Ref: https://github.com/vimeo/psalm/pull/8722#discussion_r1027151421
2022-12-06 11:19:16 +01:00
Marco Pivetta
d5cccbade2 Marking ReflectionProperty#$name as string rather than non-empty-string
Because @weirdan is a party pooper (they poop at the parties)

Ref: https://www.youtube.com/watch?v=gjwofYhUJEM
Ref: https://github.com/vimeo/psalm/pull/8722#discussion_r1027151708
2022-12-06 11:12:01 +01:00
Marco Pivetta
322cff6f43 Declaring more precise types and purity boundaries on ext-reflection symbols in .phpstub files
Also:

 * added PHP 8.2 stubs
 * refined types to make impossible scenarios more clear (like `ReflectionIntersectionType#allowsNull()`)

This is a first attempt at refining these types: the structure of these stubs is quite confusing to me,
so I don't know if this approach is correct, and if the stubs are merged together, or if entire symbols
need to be completely re-declared for each PHP version.
2022-12-06 11:08:30 +01:00
Mark McEver
9764803c55 Allowed taints to pass through urlencode() 2022-12-05 17:25:36 -06:00