Skip to content

Commit

Permalink
Merge pull request #8722 from Ocramius/feature/#8720-improve-types-an…
Browse files Browse the repository at this point in the history
…d-purity-for-reflection-symbols

Declaring more precise types and purity boundaries on `ext-reflection` symbols in `.phpstub` files
  • Loading branch information
orklah committed Dec 12, 2022
2 parents fb685a1 + 68d88c5 commit 72e7386
Show file tree
Hide file tree
Showing 6 changed files with 649 additions and 44 deletions.
19 changes: 17 additions & 2 deletions src/Psalm/Config.php
Expand Up @@ -2085,6 +2085,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 Expand Up @@ -2125,16 +2135,21 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null):
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'SPL.phpstub',
];

if (PHP_VERSION_ID >= 8_00_00 && $codebase->analysis_php_version_id >= 8_00_00) {
if ($codebase->analysis_php_version_id >= 8_00_00) {
$stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php80.phpstub';
$this->internal_stubs[] = $stringable_path;
}

if (PHP_VERSION_ID >= 8_01_00 && $codebase->analysis_php_version_id >= 8_01_00) {
if ($codebase->analysis_php_version_id >= 8_01_00) {
$stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php81.phpstub';
$this->internal_stubs[] = $stringable_path;
}

if ($codebase->analysis_php_version_id >= 8_02_00) {
$stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php82.phpstub';
$this->internal_stubs[] = $stringable_path;
}

foreach ($this->php_extensions as $ext => $enabled) {
if ($enabled) {
$this->internal_stubs[] = $dir_lvl_2 . DIRECTORY_SEPARATOR . "stubs"
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
48 changes: 34 additions & 14 deletions stubs/Php81.phpstub
Expand Up @@ -26,6 +26,32 @@ namespace {
public static function tryFrom(string|int $value): ?static;
}

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

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 {}
}

/** @psalm-immutable */
class ReflectionEnum extends ReflectionClass implements Reflector
{
public function getBackingType(): ?ReflectionType;
Expand All @@ -36,32 +62,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<ReflectionNamedType> */
public function getTypes(): array {}

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

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

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

0 comments on commit 72e7386

Please sign in to comment.