Skip to content

Commit

Permalink
ReflectionClass stub with lazy object methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 16, 2025
1 parent 1d63c05 commit 25ec5eb
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 14 deletions.
6 changes: 5 additions & 1 deletion conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ parameters:
- stdClass
stubFiles:
- ../stubs/ReflectionAttribute.stub
- ../stubs/ReflectionClass.stub
- ../stubs/ReflectionClassConstant.stub
- ../stubs/ReflectionFunctionAbstract.stub
- ../stubs/ReflectionMethod.stub
Expand Down Expand Up @@ -418,6 +417,11 @@ services:
tags:
- phpstan.stubFilesExtension

-
class: PHPStan\PhpDoc\ReflectionClassStubFilesExtension
tags:
- phpstan.stubFilesExtension

-
class: PHPStan\PhpDoc\ReflectionEnumStubFilesExtension
tags:
Expand Down
5 changes: 5 additions & 0 deletions src/Php/PhpVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,11 @@ public function supportsAsymmetricVisibility(): bool
return $this->versionId >= 80400;
}

public function supportsLazyObjects(): bool
{
return $this->versionId >= 80400;
}

public function hasDateTimeExceptions(): bool
{
return $this->versionId >= 80300;
Expand Down
27 changes: 27 additions & 0 deletions src/PhpDoc/ReflectionClassStubFilesExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDoc;

use PHPStan\Php\PhpVersion;

final class ReflectionClassStubFilesExtension implements StubFilesExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function getFiles(): array
{
if (!$this->phpVersion->supportsLazyObjects()) {
return [
__DIR__ . '/../../stubs/ReflectionClass.stub',
];
}

return [
__DIR__ . '/../../stubs/ReflectionClassWithLazyObjects.stub',
];
}

}
6 changes: 5 additions & 1 deletion src/PhpDoc/ReflectionEnumStubFilesExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ public function getFiles(): array
return [];
}

return [__DIR__ . '/../../stubs/ReflectionEnum.stub'];
if (!$this->phpVersion->supportsLazyObjects()) {
return [__DIR__ . '/../../stubs/ReflectionEnum.stub'];
}

return [__DIR__ . '/../../stubs/ReflectionEnumWithLazyObjects.stub'];
}

}
113 changes: 113 additions & 0 deletions stubs/ReflectionClassWithLazyObjects.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

/**
* @template T of object
*/
class ReflectionClass
{

/**
* @readonly
* @var class-string<T>
*/
public $name;

/**
* @param T|class-string<T> $argument
* @throws ReflectionException
*/
public function __construct($argument) {}

/**
* @return class-string<T>
*/
public function getName() : string;

/**
* @param mixed ...$args
*
* @return T
*/
public function newInstance(...$args) {}

/**
* @param array<int|string, mixed> $args
*
* @return T
*/
public function newInstanceArgs(array $args) {}

/**
* @return T
*/
public function newInstanceWithoutConstructor();

/**
* @return list<ReflectionAttribute<object>>
*/
public function getAttributes(?string $name = null, int $flags = 0)
{
}

/**
* @param callable(T): void $initializer
* @return T
*/
public function newLazyGhost(callable $initializer, int $options = 0): object
{
}

/**
* @param callable(T): T $factory
* @return T
*/
public function newLazyProxy(callable $factory, int $options = 0): object
{
}

/**
* @param T $object
* @param callable(T): void $initializer
*/
public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void
{
}

/**
* @param T $object
* @param callable(T): T $factory
*/
public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void
{
}

/**
* @param T $object
* @return T
*/
public function initializeLazyObject(object $object): object
{
}

/**
* @param T $object
* @return T
*/
public function markLazyObjectAsInitialized(object $object): object
{
}

/**
* @param T $object
*/
public function getLazyInitializer(object $object): ?callable
{
}

/**
* @param T $object
*/
public function isUninitializedLazyObject(object $object): bool
{
}
}
2 changes: 1 addition & 1 deletion stubs/ReflectionEnum.stub
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ReflectionEnum extends ReflectionClass
public function getCase(string $name): ReflectionEnumUnitCase {}

/**
* @phpstan-assert-if-true self<BackedEnum> $this
* @phpstan-assert-if-true self<T&BackedEnum> $this
* @phpstan-assert-if-true !null $this->getBackingType()
*/
public function isBacked(): bool {}
Expand Down
27 changes: 27 additions & 0 deletions stubs/ReflectionEnumWithLazyObjects.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/**
* @template T of UnitEnum
* @extends ReflectionClass<T>
*/
class ReflectionEnum extends ReflectionClass
{

/**
* @return (T is BackedEnum ? ReflectionEnumBackedCase[] : ReflectionEnumUnitCase[])
*/
public function getCases(): array {}

/**
* @return (T is BackedEnum ? ReflectionEnumBackedCase : ReflectionEnumUnitCase)
* @throws ReflectionException
*/
public function getCase(string $name): ReflectionEnumUnitCase {}

/**
* @phpstan-assert-if-true self<T&BackedEnum> $this
* @phpstan-assert-if-true !null $this->getBackingType()
*/
public function isBacked(): bool {}

}
16 changes: 16 additions & 0 deletions tests/PHPStan/Analyser/nsrt/enum-reflection-backed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php // lint < 8.4

namespace EnumReflection;

use ReflectionEnum;
use UnitEnum;
use function PHPStan\Testing\assertType;

/** @param class-string<UnitEnum> $class */
function testNarrowGetNameTypeAfterIsBacked(string $class) {
$r = new ReflectionEnum($class);
assertType('class-string<UnitEnum>', $r->getName());
if ($r->isBacked()) {
assertType('class-string<BackedEnum>', $r->getName());
}
}
9 changes: 0 additions & 9 deletions tests/PHPStan/Analyser/nsrt/enum-reflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,3 @@ public function doFoo(): void
}

}

/** @param class-string<UnitEnum> $class */
function testNarrowGetNameTypeAfterIsBacked(string $class) {
$r = new ReflectionEnum($class);
assertType('class-string<UnitEnum>', $r->getName());
if ($r->isBacked()) {
assertType('class-string<BackedEnum>', $r->getName());
}
}
5 changes: 3 additions & 2 deletions tests/PHPStan/Type/Generic/GenericObjectTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Traversable;
use function array_map;
use function sprintf;
use const PHP_VERSION_ID;

class GenericObjectTypeTest extends PHPStanTestCase
{
Expand Down Expand Up @@ -134,7 +135,7 @@ public function dataIsSuperTypeOf(): array
new GenericObjectType(ReflectionClass::class, [
new ObjectType(stdClass::class),
]),
TrinaryLogic::createYes(),
PHP_VERSION_ID >= 80400 ? TrinaryLogic::createNo() : TrinaryLogic::createYes(),
],
[
new GenericObjectType(ReflectionClass::class, [
Expand All @@ -143,7 +144,7 @@ public function dataIsSuperTypeOf(): array
new GenericObjectType(ReflectionClass::class, [
new ObjectWithoutClassType(),
]),
TrinaryLogic::createMaybe(),
PHP_VERSION_ID >= 80400 ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe(),
],
[
new GenericObjectType(ReflectionClass::class, [
Expand Down

0 comments on commit 25ec5eb

Please sign in to comment.