Skip to content

Commit

Permalink
Declaring more precise types and purity boundaries on `ext-reflection…
Browse files Browse the repository at this point in the history
…` 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.
  • Loading branch information
Ocramius committed Nov 19, 2022
1 parent 4e17585 commit e4eb00b
Show file tree
Hide file tree
Showing 5 changed files with 620 additions and 42 deletions.
10 changes: 10 additions & 0 deletions src/Psalm/Config.php
Expand Up @@ -2107,6 +2107,16 @@ public function visitPreloadedStubFiles(Codebase $codebase, ?Progress $progress
$core_generic_files[] = $stringable_path;
}

if (PHP_VERSION_ID < 8_02_00 && $codebase->analysis_php_version_id >= 8_02_00) {
$stringable_path = dirname(__DIR__, 2) . '/stubs/Php82.phpstub';

if (!file_exists($stringable_path)) {
throw new UnexpectedValueException('Cannot locate PHP 8.2 classes');
}

$core_generic_files[] = $stringable_path;
}

$stub_files = array_merge($core_generic_files, $this->preloaded_stub_files);

if (!$stub_files) {
Expand Down
113 changes: 110 additions & 3 deletions stubs/Php80.phpstub
Expand Up @@ -17,14 +17,24 @@ class ReflectionAttribute
{
}

/**
* @return non-empty-string
*
* @psalm-pure
*/
public function getName() : string
{
}

/**
* @psalm-pure
* @return int-mask-of<Attribute::TARGET_*>
*/
public function getTarget() : int
{
}

/** @psalm-pure */
public function isRepeated() : bool
{
}
Expand All @@ -48,13 +58,101 @@ class ReflectionAttribute
}
}

/**
* @template-covariant T as object
*
* @property-read class-string<T> $name
*/
class ReflectionClass implements Reflector {
/**
* @return non-empty-string|false
* @psalm-pure
*/
public function getFileName(): string|false {}

/**
* @return positive-int|false
* @psalm-pure
*/
public function getStartLine(): int|false {}

/**
* @return positive-int|false
* @psalm-pure
*/
public function getEndLine(): int|false {}

/**
* @return non-empty-string|false
* @psalm-pure
*/
public function getDocComment(): string|false {}

/**
* @param ReflectionClass|class-string $class
*
* @psalm-pure
*/
public function isSubclassOf(self|string $class): bool {}

/**
* @param self|class-string $interface
*
* @psalm-pure
*/
public function implementsInterface(self|string $interface): bool {}

/**
* @return non-empty-string|false
*
* @psalm-pure
*/
public function getExtensionName(): string|false {}
}

/** @psalm-immutable */
class ReflectionClassConstant
{
public const IS_PUBLIC = 1;
public const IS_PROTECTED = 2;
public const IS_PRIVATE = 4;

/** @return non-empty-string|false */
public function getDocComment(): string|false {}
}

abstract class ReflectionFunctionAbstract implements Reflector
{
/**
* @return non-empty-string|false
*
* @psalm-pure
*/
public function getDocComment(): string|false {}

/**
* @return positive-int|false
*
* @psalm-pure
*/
public function getStartLine(): int|false {}

/**
* @return positive-int|false
*
* @psalm-pure
*/
public function getEndLine(): int|false {}

/**
* @return non-empty-string|false
*
* @psalm-pure
*/
public function getFileName(): string|false {}
}

/** @psalm-immutable */
class Attribute
{
public int $flags;
Expand All @@ -76,11 +174,20 @@ class Attribute
}
}

class ReflectionUnionType extends ReflectionType {
class ReflectionProperty implements Reflector
{
/**
* @return non-empty-list<ReflectionNamedType>
* @return non-empty-string|false
*
* @psalm-pure
*/
public function getTypes() {}
public function getDocComment(): string|false {}
}

/** @psalm-immutable */
class ReflectionUnionType extends ReflectionType {
/** @return non-empty-list<ReflectionNamedType> */
public function getTypes(): array {}
}

class UnhandledMatchError extends Error {}
Expand Down
28 changes: 14 additions & 14 deletions stubs/Php81.phpstub
Expand Up @@ -26,6 +26,12 @@ namespace {
public static function tryFrom(string|int $value): ?static;
}

class ReflectionClass implements Reflector {
/** @psalm-pure */
public function isEnum(): bool {}
}

/** @psalm-immutable */
class ReflectionEnum extends ReflectionClass implements Reflector
{
public function getBackingType(): ?ReflectionType;
Expand All @@ -36,32 +42,26 @@ namespace {
public function isBacked(): bool;
}

/** @psalm-immutable */
class ReflectionEnumUnitCase extends ReflectionClassConstant implements Reflector
{
/**
* @psalm-pure
*/
public function getEnum(): ReflectionEnum;

/**
* @psalm-pure
*/
public function getValue(): UnitEnum;
}

/** @psalm-immutable */
class ReflectionEnumBackedCase extends ReflectionEnumUnitCase implements Reflector
{
/**
* @psalm-pure
*/
public function getBackingValue(): int|string;
}

/** @psalm-immutable */
class ReflectionIntersectionType extends ReflectionType {
/**
* @return non-empty-list<ReflectionType>
*/
public function getTypes() {}
/** @return non-empty-list<ReflectionType> */
public function getTypes(): array {}

/** @return false */
public function allowsNull(): bool {}
}
}

Expand Down
7 changes: 7 additions & 0 deletions stubs/Php82.phpstub
@@ -0,0 +1,7 @@
<?php
namespace {
class ReflectionClass implements Reflector {
/** @psalm-pure */
public function isReadOnly(): bool {}
}
}

0 comments on commit e4eb00b

Please sign in to comment.