diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index d3c3808..7e5d3b6 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [8.1, 8.2, 8.3] + php: [8.1, 8.2, 8.3, 8.4] dependency-versions: [locked, lowest, highest] steps: - uses: actions/checkout@v4 @@ -76,7 +76,7 @@ jobs: with: composer-options: --optimize-autoloader dependency-versions: ${{ matrix.dependency-versions }} - - run: composer test -- --colors=always --coverage-clover coverage.xml + - run: composer test -- --colors=always --coverage-clover=coverage.xml ${{ matrix.php == '8.4' && '--testsuite=php84' || '' }} - if: ${{ matrix.php == '8.1' && matrix.dependency-versions == 'locked' }} uses: codecov/codecov-action@v4 env: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3798c0d..6962eaa 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -11,7 +11,7 @@ > - + @@ -19,6 +19,11 @@ tests + + tests + tests/Internal/NativeAdapter/AdapterCompatibilityTest.php + + tests/Internal/Inheritance/TypeResolversTest.php tests/Internal/PhpDoc/PhpDocTypeReflectorTest.php diff --git a/psalm.xml.dist b/psalm.xml.dist index 49d36da..facdcd2 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -115,5 +115,6 @@ + diff --git a/src/Internal/NativeAdapter/ClassAdapter.php b/src/Internal/NativeAdapter/ClassAdapter.php index 6406791..1079116 100644 --- a/src/Internal/NativeAdapter/ClassAdapter.php +++ b/src/Internal/NativeAdapter/ClassAdapter.php @@ -525,6 +525,86 @@ public function setStaticPropertyValue(string $name, mixed $value): void parent::setStaticPropertyValue($name, $value); } + /** + * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function newLazyGhost(callable $initializer, int $options = 0): object + { + $this->loadNative(); + + return parent::newLazyGhost($initializer, $options); + } + + /** + * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function newLazyProxy(callable $factory, int $options = 0): object + { + $this->loadNative(); + + return parent::newLazyProxy($factory, $options); + } + + /** + * @psalm-suppress UndefinedMethod + */ + public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void + { + $this->loadNative(); + + parent::resetAsLazyGhost($object, $initializer, $options); + } + + /** + * @psalm-suppress UndefinedMethod + */ + public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void + { + $this->loadNative(); + + parent::resetAsLazyProxy($object, $factory, $options); + } + + /** + * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function initializeLazyObject(object $object): object + { + $this->loadNative(); + + return parent::initializeLazyObject($object); + } + + /** + * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function markLazyObjectAsInitialized(object $object): object + { + $this->loadNative(); + + return parent::markLazyObjectAsInitialized($object); + } + + /** + * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function getLazyInitializer(object $object): ?callable + { + $this->loadNative(); + + return parent::getLazyInitializer($object); + } + + /** + * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function isUninitializedLazyObject(object $object): bool + { + $this->loadNative(); + + return parent::isUninitializedLazyObject($object); + } + /** * @return Properties */ diff --git a/src/Internal/NativeAdapter/ClassConstantAdapter.php b/src/Internal/NativeAdapter/ClassConstantAdapter.php index 6a56511..f16b1d4 100644 --- a/src/Internal/NativeAdapter/ClassConstantAdapter.php +++ b/src/Internal/NativeAdapter/ClassConstantAdapter.php @@ -124,6 +124,15 @@ public function hasType(): bool return $this->reflection->type(TypeKind::Native) !== null; } + /** + * @psalm-suppress PossiblyUnusedMethod, UnusedPsalmSuppress + * @TODO + */ + public function isDeprecated(): bool + { + return false; + } + public function isEnumCase(): bool { return $this->reflection->isEnumCase(); diff --git a/src/Internal/NativeAdapter/EnumAdapter.php b/src/Internal/NativeAdapter/EnumAdapter.php index 7015046..a695a66 100644 --- a/src/Internal/NativeAdapter/EnumAdapter.php +++ b/src/Internal/NativeAdapter/EnumAdapter.php @@ -361,4 +361,68 @@ public function setStaticPropertyValue(string $name, mixed $value): void { $this->_class->setStaticPropertyValue($name, $value); } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function newLazyGhost(callable $initializer, int $options = 0): object + { + return $this->_class->newLazyGhost($initializer, $options); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function newLazyProxy(callable $factory, int $options = 0): object + { + return $this->_class->newLazyProxy($factory, $options); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void + { + $this->_class->resetAsLazyGhost($object, $initializer, $options); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void + { + $this->_class->resetAsLazyProxy($object, $factory, $options); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function initializeLazyObject(object $object): object + { + return $this->_class->initializeLazyObject($object); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function markLazyObjectAsInitialized(object $object): object + { + return $this->_class->markLazyObjectAsInitialized($object); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function getLazyInitializer(object $object): ?callable + { + return $this->_class->getLazyInitializer($object); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function isUninitializedLazyObject(object $object): bool + { + return $this->_class->isUninitializedLazyObject($object); + } } diff --git a/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php b/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php index dffaac4..e11dbda 100644 --- a/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php +++ b/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php @@ -102,6 +102,14 @@ public function hasType(): bool return $this->constant->hasType(); } + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function isDeprecated(): bool + { + return $this->constant->isDeprecated(); + } + public function isEnumCase(): bool { return $this->constant->isEnumCase(); diff --git a/src/Internal/NativeAdapter/PropertyAdapter.php b/src/Internal/NativeAdapter/PropertyAdapter.php index 79155dc..c9f618c 100644 --- a/src/Internal/NativeAdapter/PropertyAdapter.php +++ b/src/Internal/NativeAdapter/PropertyAdapter.php @@ -179,6 +179,154 @@ public function setValue(mixed $objectOrValue, mixed $value = null): void parent::setValue($objectOrValue, $value); } + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function getHooks(): array + { + $this->loadNative(); + + return parent::getHooks(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function getHook(\PropertyHookType $hook): ?\ReflectionMethod + { + $this->loadNative(); + + return parent::getHook($hook); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function isVirtual(): bool + { + $this->loadNative(); + + return parent::isVirtual(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function getSettableType(): ?\ReflectionType + { + $this->loadNative(); + + return parent::getSettableType(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod + */ + public function getRawValue(object $object): mixed + { + $this->loadNative(); + + return parent::getRawValue($object); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod + */ + public function setRawValue(object $object, mixed $value): void + { + $this->loadNative(); + + parent::setRawValue($object, $value); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod + */ + public function setRawValueWithoutLazyInitialization(object $object, mixed $value): void + { + $this->loadNative(); + + parent::setRawValueWithoutLazyInitialization($object, $value); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod + */ + public function skipLazyInitialization(object $object): void + { + $this->loadNative(); + + parent::skipLazyInitialization($object); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function isPrivateSet(): bool + { + $this->loadNative(); + + return parent::isPrivateSet(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function isProtectedSet(): bool + { + $this->loadNative(); + + return parent::isProtectedSet(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function isDynamic(): bool + { + return false; + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function isAbstract(): bool + { + $this->loadNative(); + + return parent::isAbstract(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function isFinal(): bool + { + $this->loadNative(); + + return parent::isFinal(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function hasHooks(): bool + { + $this->loadNative(); + + return parent::hasHooks(); + } + + /** + * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod + */ + public function hasHook(\PropertyHookType $hook): bool + { + $this->loadNative(); + + return parent::hasHook($hook); + } + private bool $nativeLoaded = false; private function loadNative(): void diff --git a/src/Internal/NativeAdapter/PropertyHookType.php b/src/Internal/NativeAdapter/PropertyHookType.php new file mode 100644 index 0000000..d1eb16e --- /dev/null +++ b/src/Internal/NativeAdapter/PropertyHookType.php @@ -0,0 +1,13 @@ +hasType(), $typhoon->hasType(), $messagePrefix . '.hasType()'); } + if (method_exists(\ReflectionClassConstant::class, 'isDeprecated')) { + /** @psalm-suppress MixedArgument, UnusedPsalmSuppress */ + self::assertSame($native->isDeprecated(), $typhoon->isDeprecated(), $messagePrefix . '.isDeprecated()'); + } self::assertSame($native->isEnumCase(), $typhoon->isEnumCase(), $messagePrefix . '.isEnumCase()'); self::assertSame($native->isFinal(), $typhoon->isFinal(), $messagePrefix . '.isFinal()'); self::assertSame($native->isPrivate(), $typhoon->isPrivate(), $messagePrefix . '.isPrivate()'); @@ -336,6 +340,10 @@ private static function assertPropertyEquals(\ReflectionProperty $native, \Refle self::assertSame($native->hasDefaultValue(), $typhoon->hasDefaultValue(), $messagePrefix . '.hasDefaultValue()'); self::assertSame($native->hasType(), $typhoon->hasType(), $messagePrefix . '.hasType()'); self::assertSame($native->isDefault(), $typhoon->isDefault(), $messagePrefix . '.isDefault()'); + if (method_exists(\ReflectionProperty::class, 'isDynamic')) { + /** @psalm-suppress MixedArgument, UnusedPsalmSuppress */ + self::assertSame($native->isDynamic(), $typhoon->isDynamic(), $messagePrefix . '.isDynamic()'); + } // TODO isInitialized() self::assertSame($native->isPrivate(), $typhoon->isPrivate(), $messagePrefix . '.isPrivate()'); self::assertSame($native->isPromoted(), $typhoon->isPromoted(), $messagePrefix . '.isPromoted()');