2021-05-03 23:54:09 +02:00
|
|
|
<?php
|
2021-09-04 22:44:06 +02:00
|
|
|
namespace {
|
|
|
|
interface UnitEnum {
|
2021-11-22 13:38:08 +01:00
|
|
|
/** @var non-empty-string $name */
|
2021-11-22 13:47:36 +01:00
|
|
|
public readonly string $name;
|
2021-11-28 08:37:57 +01:00
|
|
|
|
2021-12-20 23:20:50 +01:00
|
|
|
/**
|
|
|
|
* @psalm-pure
|
2021-12-21 12:06:41 +01:00
|
|
|
* @return list<static>
|
2021-12-20 23:20:50 +01:00
|
|
|
*/
|
2021-09-04 22:44:06 +02:00
|
|
|
public static function cases(): array;
|
|
|
|
}
|
2021-11-28 08:37:57 +01:00
|
|
|
|
2022-02-22 23:52:53 +01:00
|
|
|
interface BackedEnum extends UnitEnum
|
2021-11-28 08:37:57 +01:00
|
|
|
{
|
2022-12-21 16:06:11 +01:00
|
|
|
/** @var non-empty-string $name */
|
|
|
|
public readonly string $name;
|
2021-11-28 08:37:57 +01:00
|
|
|
public readonly int|string $value;
|
2021-12-20 23:20:50 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @psalm-pure
|
|
|
|
*/
|
2021-11-28 08:37:57 +01:00
|
|
|
public static function from(string|int $value): static;
|
2021-12-20 23:20:50 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @psalm-pure
|
|
|
|
*/
|
2021-11-28 08:37:57 +01:00
|
|
|
public static function tryFrom(string|int $value): ?static;
|
2023-01-06 02:35:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @psalm-pure
|
|
|
|
* @return list<static>
|
|
|
|
*/
|
|
|
|
public static function cases(): array;
|
2021-11-28 08:37:57 +01:00
|
|
|
}
|
2021-11-28 10:41:43 +01:00
|
|
|
|
2022-11-19 15:54:02 +01:00
|
|
|
class ReflectionClass implements Reflector {
|
|
|
|
/** @psalm-pure */
|
|
|
|
public function isEnum(): bool {}
|
|
|
|
}
|
|
|
|
|
2022-12-07 14:22:15 +01:00
|
|
|
class ReflectionProperty implements Reflector
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Starting from PHP 8.1, this method is pure, and has no effect.
|
|
|
|
*
|
|
|
|
* @psalm-pure
|
|
|
|
*/
|
|
|
|
public function setAccessible(bool $accessible): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ReflectionMethod extends ReflectionFunctionAbstract
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Starting from PHP 8.1, this method is pure, and has no effect.
|
|
|
|
*
|
|
|
|
* @psalm-pure
|
|
|
|
*/
|
|
|
|
public function setAccessible(bool $accessible): void {}
|
|
|
|
}
|
|
|
|
|
2022-11-19 15:54:02 +01:00
|
|
|
/** @psalm-immutable */
|
2021-11-28 10:41:43 +01:00
|
|
|
class ReflectionEnum extends ReflectionClass implements Reflector
|
|
|
|
{
|
|
|
|
public function getBackingType(): ?ReflectionType;
|
|
|
|
public function getCase(string $name): ReflectionEnumUnitCase;
|
|
|
|
/** @return list<ReflectionEnumUnitCase> */
|
|
|
|
public function getCases(): array;
|
|
|
|
public function hasCase(string $name): bool;
|
|
|
|
public function isBacked(): bool;
|
|
|
|
}
|
|
|
|
|
2022-11-19 15:54:02 +01:00
|
|
|
/** @psalm-immutable */
|
2021-11-28 10:41:43 +01:00
|
|
|
class ReflectionEnumUnitCase extends ReflectionClassConstant implements Reflector
|
|
|
|
{
|
|
|
|
public function getEnum(): ReflectionEnum;
|
|
|
|
public function getValue(): UnitEnum;
|
|
|
|
}
|
|
|
|
|
2022-11-19 15:54:02 +01:00
|
|
|
/** @psalm-immutable */
|
2021-11-28 10:41:43 +01:00
|
|
|
class ReflectionEnumBackedCase extends ReflectionEnumUnitCase implements Reflector
|
|
|
|
{
|
|
|
|
public function getBackingValue(): int|string;
|
|
|
|
}
|
2022-02-09 19:32:17 +01:00
|
|
|
|
2022-11-19 15:54:02 +01:00
|
|
|
/** @psalm-immutable */
|
2022-02-09 19:32:17 +01:00
|
|
|
class ReflectionIntersectionType extends ReflectionType {
|
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
|
|
|
/** @return non-empty-list<ReflectionNamedType> */
|
2022-11-19 15:54:02 +01:00
|
|
|
public function getTypes(): array {}
|
|
|
|
|
|
|
|
/** @return false */
|
|
|
|
public function allowsNull(): bool {}
|
2022-02-09 19:32:17 +01:00
|
|
|
}
|
2021-09-04 22:44:06 +02:00
|
|
|
}
|
2021-05-03 23:54:09 +02:00
|
|
|
|
2021-09-04 22:44:06 +02:00
|
|
|
namespace FTP {
|
|
|
|
final class Connection {}
|
2021-05-03 23:54:09 +02:00
|
|
|
}
|
2021-10-02 11:58:08 +02:00
|
|
|
|
|
|
|
namespace IMAP {
|
|
|
|
final class Connection {}
|
2021-10-02 19:30:50 +02:00
|
|
|
}
|
|
|
|
|
2021-10-03 07:52:16 +02:00
|
|
|
namespace LDAP {
|
2021-10-02 19:30:50 +02:00
|
|
|
final class Connection {}
|
|
|
|
final class Result {}
|
|
|
|
final class ResultEntry {}
|
2021-10-04 13:41:31 +02:00
|
|
|
}
|
|
|
|
|
2021-10-14 19:30:03 +02:00
|
|
|
namespace PgSql {
|
|
|
|
final class Connection {}
|
|
|
|
final class Result {}
|
|
|
|
final class Lob {}
|
|
|
|
}
|
2021-10-14 20:38:26 +02:00
|
|
|
|
|
|
|
namespace PSpell {
|
|
|
|
final class Config {}
|
|
|
|
final class Dictionary {}
|
|
|
|
}
|